AWS: 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:
| Refused | Use instead |
|---|---|
SELECT | Separate databases for separate data sets |
SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE and their UNSUBSCRIBE forms, MONITOR | A data-port connection (PUBLISH works over REST) |
MULTI, EXEC, DISCARD, WATCH, UNWATCH | /multi-exec |
CLIENT, HELLO, AUTH, RESET, QUIT | Nothing: the token authenticates each request |
BLPOP, BRPOP, BLMOVE, BLMPOP, BRPOPLPUSH, BZPOPMIN, BZPOPMAX, BZMPOP, WAIT, WAITAOF, XREAD and XREADGROUP with BLOCK | The non-blocking form (LPOP, XREAD without BLOCK), polled |
Limits
| Limit | |
|---|---|
| Request body | 10 MB |
| Reply | 64 MB |
| Commands in one pipeline or transaction | 1,000 |
| Time per request | 10 seconds |
| Failed authentications | 30 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.