Clients
Connecting to a CapyDB K/V store over the Upstash REST protocol or RESP - @upstash/redis, @capydb/kv, ioredis, redis-py, go-redis and redis-cli.
A K/V store speaks two protocols. Both are standard, so migrating in is a URL and a token.
| Protocol | Endpoint | Use it from |
|---|---|---|
| Upstash REST over HTTPS | CAPYKV_REST_URL | Serverless and edge runtimes, anything without a connection pool |
| RESP over TLS | CAPYKV_REDIS_URL | Long-lived servers, blocking commands, pub/sub |
Upstash REST
The REST protocol carries pipelining, transactions and base64 encoding, so @upstash/redis works at
its default settings - including Upstash-Encoding: base64 and auto-pipelining, both of which the
client turns on for you.
import { Redis } from '@upstash/redis'
const redis = new Redis({
url: process.env.CAPYKV_REST_URL!,
token: process.env.CAPYKV_REST_TOKEN!,
})
await redis.set('user:1', { name: 'Ada' })
const user = await redis.get<{ name: string }>('user:1')
const [count, ttl] = await redis
.pipeline()
.incr('visits')
.ttl('visits')
.exec()Redis.fromEnv() reads UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN. CapyDB publishes
its own names, so either construct the client explicitly or set the UPSTASH_* names yourself.
Calling the endpoint directly, without a client:
curl "$CAPYKV_REST_URL/set/greeting/hello" \
-H "Authorization: Bearer $CAPYKV_REST_TOKEN"
curl "$CAPYKV_REST_URL" \
-H "Authorization: Bearer $CAPYKV_REST_TOKEN" \
-d '["GET", "greeting"]'@capydb/kv
@capydb/kv is a small factory that reads the CapyDB variable names and returns a configured
@upstash/redis client. It exists so you do not hand-wire the same three lines in every service; it
is not a wrapper, and the object it returns is an @upstash/redis client with nothing added.
npm install @capydb/kv @upstash/redisimport { createKv } from '@capydb/kv'
const kv = createKv()
await kv.set('hello', 'world')It reads CAPYKV_REST_URL / CAPYKV_REST_TOKEN, falling back to UPSTASH_REDIS_REST_URL /
UPSTASH_REDIS_REST_TOKEN so an existing Upstash deployment keeps working. It refuses a plaintext
http:// URL unless you opt in explicitly.
RESP
Ordinary Redis® OSS clients connect over TLS on port 6379, with the token as the password. Blocking commands and pub/sub work here and not over REST.
The RESP URL is published as CAPYKV_REDIS_URL. It embeds the token as its password, so like
the token it exists in full only on the create and rotate responses: capydb kv create --write-env
and capydb kv rotate-token --write-env put it in your env file next to the REST pair, and the
dashboard shows it once, alongside the token. The deployment integrations cannot push it, for the
same reason they cannot push the token.
The endpoint routes by TLS server name (SNI). One address serves every store, and the server
name in the TLS handshake is what picks yours. A client that connects without one is refused
with ERR TLS handshake carried no server name (SNI) … - that is not a hostname or token
problem. Node's tls.connect sends no server name unless servername is set, so ioredis,
and everything built on it (BullMQ included), needs tls: { servername }; redis-cli and
valkey-cli need --sni. redis-py and go-redis send it on their own.
import Redis from 'ioredis'
const url = process.env.CAPYKV_REDIS_URL!
const redis = new Redis(url, { tls: { servername: new URL(url).hostname } })
const job = await redis.blpop('queue', 0)import redis
r = redis.from_url(os.environ["CAPYKV_REDIS_URL"])
r.set("hello", "world")opt, err := redis.ParseURL(os.Getenv("CAPYKV_REDIS_URL"))
if err != nil {
return err
}
client := redis.NewClient(opt)redis-cli --tls --sni <project>.db.capydb.dev -u "$CAPYKV_REDIS_URL" PINGGET .../kv/credentials returns the RESP endpoint without a password and sets token_required: true, because only the token's hash is stored. If you no longer have the token, rotate.
Commands that are not available
The store's ACL denies the administrative and replication surface. Everything a client application does is allowed; everything that would reconfigure the cell, take over its replication, or read another tenant's traffic is not:
CONFIG, SHUTDOWN, DEBUG, MODULE, REPLICAOF / SLAVEOF, SAVE / BGSAVE /
BGREWRITEAOF, ACL, CLUSTER, MIGRATE, SYNC / PSYNC, MONITOR, FAILOVER, RESET.
Persistence and eviction are managed by CapyDB, which is why CONFIG and the SAVE family are not
yours to call - see Operations.
Migrating from Upstash
Change two variables. The REST protocol, the client, the limiter and the command surface are the same; what changes is the region the store sits in and the fact that it is billed as part of your plan rather than per command.
If you already read UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN throughout your code,
set those names to CapyDB's values and change nothing else.