Azure: REST API

Every Essentials and Pro database can also take commands over HTTPS. It is meant for the places a TCP connection is awkward or impossible: Vercel and…

REST API

Every Essentials and Pro database can also take commands over HTTPS. It is meant for the places a TCP connection is awkward or impossible: Vercel and Netlify functions, Cloudflare Workers and other edge runtimes, and browsers.

The REST API speaks the same wire format as Upstash's. An app that uses the @upstash/redis SDK moves to KiviDB by changing two environment variables, with no code change.

Turning it on

Configuration → REST API, then switch it on. KiviDB issues a public certificate for your database's hostname and opens port 443, which takes a few minutes; the card shows Setting up until it is Ready. Once it is ready, the card shows the URL, a token for any of the database's users, and snippets to copy.

Port 443 follows your database's network rules exactly like the data ports: an address that cannot reach port 6379 or 6380 cannot reach the REST API either.

Turning it off closes port 443 at once. Connections on the data ports are not affected.

The REST API is not available on free databases or on sharded databases.

The URL

The URL is your database's hostname over HTTPS, on the standard port:

https://my-db.cloud.kividb.io

The certificate is from a public certificate authority (Let's Encrypt), so fetch() in Node, browsers and Workers accepts it without extra configuration. You do not need the KiviDB CA certificate that TLS connections on port 6380 use.

Tokens

A token is a database user's credentials: kvr_ followed by the base64url encoding (without padding) of username:password. Every database user has one, and the console shows it beside the user's password in Configuration → Users once the REST API is ready, and in the token picker on the REST API card. There is nothing extra to create or store.

A token can do exactly what its user can do, because the database itself checks every command against that user's permissions. To build one yourself, for example in a deploy script:

echo "kvr_$(printf '%s' "$USERNAME:$PASSWORD" | base64 | tr '+/' '-_' | tr -d '=\n')"

Read-only tokens

The token of a user with the Read only profile is a read-only token: reads succeed and every write is refused by the database. Because the REST API allows requests from any origin (see CORS), a read-only token can be shipped to a browser to read public data directly, such as a leaderboard or a feature flag. Keep every other token on the server.

To get one, add a user with the Read only profile in Configuration → Users → Add user, or give a user a custom role for finer control.

Revoking a token

  • Change the user's password: the old token stops working within 30 seconds, and the user's new token replaces it.
  • Delete the user: its token stops working immediately.

Moving from Upstash

With @upstash/redis, change the two variables your app reads and redeploy:

UPSTASH_REDIS_REST_URL="https://my-db.cloud.kividb.io"
UPSTASH_REDIS_REST_TOKEN="kvr_..."

The code stays as it is:

import { Redis } from "@upstash/redis";

const redis = Redis.fromEnv();

await redis.set("greeting", "hello");
const greeting = await redis.get("greeting");

If you pass the URL and token explicitly, pass the KiviDB ones:

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL,
  token: process.env.UPSTASH_REDIS_REST_TOKEN,
});

Pipelines (redis.pipeline()), transactions (redis.multi()), JSON values and automatic base64 decoding work as they do on Upstash. To move the data itself, see Migrate from Redis.

Calling it with curl

Send the token as a bearer token. Every reply is JSON: {"result": ...} on success and {"error": "..."} when the command fails.

One command

Post the command and its arguments as a JSON array:

curl https://my-db.cloud.kividb.io \
  -H "Authorization: Bearer $TOKEN" \
  -d '["SET", "user:1", "ada", "EX", 3600]'
# {"result":"OK"}

Or put the command in the path, one argument per segment, URL-encoded:

curl https://my-db.cloud.kividb.io/get/user:1 \
  -H "Authorization: Bearer $TOKEN"
# {"result":"ada"}

Where a header cannot be set, pass the token as a query parameter instead: https://my-db.cloud.kividb.io/get/user:1?_token=$TOKEN. Prefer the header where you can: URLs end up in logs.

Pipeline

Post an array of commands to /pipeline. They run in order, and the reply has one entry per command; one failing does not stop the rest.

curl https://my-db.cloud.kividb.io/pipeline \
  -H "Authorization: Bearer $TOKEN" \
  -d '[["INCR", "visits"], ["EXPIRE", "visits", 86400], ["GET", "visits"]]'
# [{"result":1},{"result":1},{"result":"1"}]

Transaction

Post the same shape to /multi-exec to run the commands as one transaction (MULTI … EXEC): no other client's command runs between them.

curl https://my-db.cloud.kividb.io/multi-exec \
  -H "Authorization: Bearer $TOKEN" \
  -d '[["DECRBY", "stock:42", 1], ["LPUSH", "orders", "42"]]'
# [{"result":9},{"result":1}]

Binary-safe replies

Add Upstash-Encoding: base64 and every string in the reply comes back base64-encoded, so values that are not valid UTF-8 survive the trip through JSON. @upstash/redis sends this header and decodes for you.

curl https://my-db.cloud.kividb.io/get/user:1 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Upstash-Encoding: base64"
# {"result":"YWRh"}

Commands

Every command the database supports works, with the permissions of the token's user, except those that would change or hold the shared connection the request runs on. They are refused with an error, as on Upstash:

RefusedUse instead
SELECTSeparate databases for separate data sets
SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE and their UNSUBSCRIBE forms, MONITORA data-port connection (PUBLISH works over REST)
MULTI, EXEC, DISCARD, WATCH, UNWATCH/multi-exec
CLIENT, HELLO, AUTH, RESET, QUITNothing: the token authenticates each request
BLPOP, BRPOP, BLMOVE, BLMPOP, BRPOPLPUSH, BZPOPMIN, BZPOPMAX, BZMPOP, WAIT, WAITAOF, XREAD and XREADGROUP with BLOCKThe non-blocking form (LPOP, XREAD without BLOCK), polled

Limits

Limit
Request body10 MB
Reply64 MB
Commands in one pipeline or transaction1,000
Time per request10 seconds
Failed authentications30 a minute from one address, then every request from it gets 429 Too Many Requests for a minute

REST requests count against the same database as connections on the data ports: memory, throughput and the commands themselves are shared.

CORS

The REST API answers cross-origin requests from any origin (Access-Control-Allow-Origin: *), so a page can call it with fetch() directly. That is safe only with a read-only token: anyone who loads the page can read the token from it.