KVS gestionado respaldado por DynamoDB
KVS gestionado es el servicio de base de datos respaldado por DynamoDB para apps backend de Sureva. Permite guardar datos clave-valor acotados a la app sin acceso directo a DynamoDB. La app llama al proxy KVS en https://kvs.sureva.com; Sureva controla autorización, aislamiento, límites de tasa y medición antes de que una petición llegue al almacenamiento.
Ruta rápida
Sección titulada «Ruta rápida»- Abre la app en el frontend de Sureva y revisa Servicios → Bases de datos.
- Confirma que KVS gestionado respaldado por DynamoDB esté disponible para el tipo de app:
api,web-ssrosse. - Creá una tabla KVS desde el frontend (por ejemplo
sessions). Crear la primera tabla activa el servicio — no hay un paso separado de “habilitar”. - Lee
SUREVA_KVS_URLy el token de la tablaSUREVA_KVS_TOKEN_<NAME>desde el entorno runtime de Lambda. - Llama a
/v1/put,/v1/gety/v1/deletedesde la Lambda de la app con AWS SigV4 yX-KVS-Authorization: Bearer <table-token>.
Las apps estáticas web no pueden usar KVS gestionado porque entregar tokens al navegador expondría credenciales de almacenamiento backend.
Qué proporciona KVS gestionado
Sección titulada «Qué proporciona KVS gestionado»| Aspecto | Comportamiento |
|---|---|
| Alcance de almacenamiento | Los valores quedan aislados por organización, app y entorno. |
| Exposición pública | La API de datos no es una API pública para navegador. En producción, las llamadas deben venir de Lambdas de apps gestionadas por Sureva. |
| Credenciales de app | El código usa SUREVA_KVS_URL y un token de tabla SUREVA_KVS_TOKEN_<NAME>; no recibe permisos de DynamoDB. |
| Límite de tasa | Un bucket RPM fijo por app-entorno protege el servicio para todos los métodos KVS. |
| Respuesta de cuota | Cuando se agota el bucket, la API responde 429 con Retry-After. |
Cómo los clientes descubren disponibilidad
Sección titulada «Cómo los clientes descubren disponibilidad»Los clientes descubren las bases de datos disponibles en el frontend de Sureva, dentro de la sección Servicios → Bases de datos de la app. Hoy, el servicio backend disponible es KVS gestionado respaldado por DynamoDB.
El frontend es dueño del provisioning y de la entrega del token. El código de aplicación no debe llamar directamente endpoints de creación del plano de control; solo consume el contrato runtime después de crear una tabla.
Para tipos de app basados en Lambda, Sureva deja disponibles estas variables runtime cuando es posible:
| Variable | Significado |
|---|---|
SUREVA_KVS_URL | URL de datos de KVS gestionado, normalmente https://kvs.sureva.com. |
SUREVA_KVS_REGION | Región AWS para la firma SigV4 — la región del gateway KVS (us-east-2). Puede diferir del AWS_REGION propio de tu app, así que firmá siempre con este valor. |
SUREVA_KVS_TOKEN_<NAME> | Token bearer para una tabla KVS. El nombre de la variable es el nombre de la tabla en mayúsculas — por ejemplo, SUREVA_KVS_TOKEN_SESSIONS para una tabla llamada sessions. Solo está presente cuando esa tabla existe. KVS es solo-tablas: no hay token por defecto. |
Llamar al plano de datos
Sección titulada «Llamar al plano de datos»Las peticiones de producción necesitan dos capas de autorización:
- AWS SigV4 en el header estándar
Authorization. API Gateway lo usa para verificar que el caller sea una Lambda autorizada de una app gestionada por Sureva. - Token bearer KVS en
X-KVS-Authorization. El proxy usa este token para vincular la petición a la organización, app y entorno correctos.
No pongas el token KVS en el header estándar Authorization en producción. SigV4 usa ese header.
Firmá con la región de SUREVA_KVS_REGION, no con el AWS_REGION propio de tu app. El gateway KVS vive en us-east-2, pero tu app puede correr en otra región (por ejemplo us-east-1); una credencial firmada para una región distinta se rechaza con 403 (Credential should be scoped to a valid region). El dominio personalizado no lleva pista de región, así que el signer no puede inferirla.
Guardar un valor
Sección titulada «Guardar un valor»POST /v1/put HTTP/1.1Host: kvs.sureva.comContent-Type: application/jsonX-KVS-Authorization: Bearer <kvs-token>Authorization: AWS4-HMAC-SHA256 ...{ "key": "settings/theme", "value": { "mode": "dark" }, "ttlSeconds": 3600}Respuesta:
{ "ok": true}Leer un valor
Sección titulada «Leer un valor»{ "key": "settings/theme"}Respuesta encontrada:
{ "found": true, "value": { "mode": "dark" }, "expiresAt": "2026-06-21T01:00:00Z"}Respuesta ausente o expirada:
{ "found": false, "expiresAt": null}Eliminar un valor
Sección titulada «Eliminar un valor»{ "key": "settings/theme"}Respuesta:
{ "deleted": true}deleted: true significa que la operación terminó correctamente. No indica que la clave existiera antes de la petición.
Ejemplo TypeScript
Sección titulada «Ejemplo TypeScript»Este helper firma cada petición para API Gateway y envía el token KVS en X-KVS-Authorization. Si tu app no usa TypeScript, usa el signer SigV4 equivalente para tu runtime.
import { Sha256 } from '@aws-crypto/sha256-js';import { defaultProvider } from '@aws-sdk/credential-provider-node';import { HttpRequest } from '@smithy/protocol-http';import { SignatureV4 } from '@smithy/signature-v4';
const baseUrl = process.env.SUREVA_KVS_URL ?? 'https://kvs.sureva.com';const region = process.env.SUREVA_KVS_REGION ?? 'us-east-2';
// KVS es solo-tablas: leé el token de la tabla que creaste.const tableName = 'sessions';const token = process.env[`SUREVA_KVS_TOKEN_${tableName.toUpperCase()}`];
const signer = new SignatureV4({ credentials: defaultProvider(), region, service: 'execute-api', sha256: Sha256,});
async function kvsRequest<T>(path: string, body: Record<string, unknown>): Promise<T> { if (!token) throw new Error(`KVS table "${tableName}" is not enabled for this app`);
const url = new URL(path, baseUrl); const payload = JSON.stringify(body);
const request = new HttpRequest({ protocol: url.protocol, hostname: url.hostname, path: url.pathname, method: 'POST', headers: { host: url.hostname, 'content-type': 'application/json', 'x-kvs-authorization': `Bearer ${token}`, }, body: payload, });
const signed = await signer.sign(request); const response = await fetch(url, { method: 'POST', headers: signed.headers, body: payload, });
if (response.status === 429) { const retryAfter = response.headers.get('retry-after'); throw new Error(`KVS rate limited; retry after ${retryAfter ?? 'a short delay'}`); }
if (!response.ok) { throw new Error(`KVS request failed: ${response.status} ${await response.text()}`); }
return response.json() as Promise<T>;}
export function putKVS(key: string, value: unknown, ttlSeconds?: number) { return kvsRequest<{ ok: true }>('/v1/put', { key, value, ttlSeconds });}
export function getKVS<T>(key: string) { return kvsRequest<{ found: boolean; value?: T; expiresAt: string | null }>('/v1/get', { key });}
export function deleteKVS(key: string) { return kvsRequest<{ deleted: true }>('/v1/delete', { key });}Namespaces de tabla con nombre
Sección titulada «Namespaces de tabla con nombre»Una app puede tener hasta 2 tablas KVS con nombre. Cada tabla tiene su propio espacio de claves aislado, token y cuota de tasa independiente. Usá tablas con nombre para separar datos por propósito — por ejemplo, sessions y cache — sin interferencia entre ellas.
Cuando se habilita una tabla con nombre, Sureva inyecta SUREVA_KVS_TOKEN_<UPPER(name)> en el entorno runtime de Lambda automáticamente. Cuando se elimina la tabla, la variable se remueve.
Para llamar al plano de datos de una tabla, usá el token de esa tabla en X-KVS-Authorization. La URL del plano de datos (SUREVA_KVS_URL) es la misma para todas las tablas.
function createTableClient(tableName: string) { const varName = `SUREVA_KVS_TOKEN_${tableName.toUpperCase()}`; const tableToken = process.env[varName]; if (!tableToken) throw new Error(`La tabla KVS '${tableName}' no está habilitada para esta app`);
return { put: (key: string, value: unknown, ttlSeconds?: number) => kvsRequestWithToken(tableToken, '/v1/put', { key, value, ttlSeconds }), get: <T>(key: string) => kvsRequestWithToken<{ found: boolean; value?: T; expiresAt: string | null }>(tableToken, '/v1/get', { key }), delete: (key: string) => kvsRequestWithToken<{ deleted: true }>(tableToken, '/v1/delete', { key }), };}
async function kvsRequestWithToken<T>(token: string, path: string, body: Record<string, unknown>): Promise<T> { const url = new URL(path, baseUrl); const payload = JSON.stringify(body); const request = new HttpRequest({ protocol: url.protocol, hostname: url.hostname, path: url.pathname, method: 'POST', headers: { host: url.hostname, 'content-type': 'application/json', 'x-kvs-authorization': `Bearer ${token}` }, body: payload, }); const signed = await signer.sign(request); const response = await fetch(url, { method: 'POST', headers: signed.headers, body: payload }); if (response.status === 429) throw new Error(`KVS con límite de tasa; reintentá después de ${response.headers.get('retry-after') ?? 'un momento'}`); if (!response.ok) throw new Error(`Petición KVS fallida: ${response.status} ${await response.text()}`); return response.json() as Promise<T>;}
// Usoconst sessions = createTableClient('sessions');await sessions.put('user:123', { role: 'admin' }, 3600);Las claves están aisladas por tabla. user:123 en sessions no interfiere con user:123 en otra tabla.
Límites y errores
Sección titulada «Límites y errores»| Regla | Valor |
|---|---|
| Cuerpo de petición | 128 KiB |
| Clave | 512 bytes |
| Valor JSON | 64 KiB |
| TTL mínimo | 60 segundos |
| TTL máximo | 365 días |
| Límite de tasa | Bucket RPM fijo por app-entorno |
Los errores del servicio usan esta forma:
{ "error": "invalid_key"}| Estado | Significado |
|---|---|
400 | JSON, clave, valor o TTL inválido. |
401 | Token bearer KVS ausente o inválido. |
403 | Binding inactivo, tipo de app no soportado o rechazo del caller por API Gateway. |
413 | El cuerpo de la petición es demasiado grande. |
429 | Límite de tasa excedido. Lee Retry-After antes de reintentar. |
500 / 503 | Falla del lado del servicio en storage, auth, cuota o dependencias. |
Checklist
Sección titulada «Checklist»- El tipo de app es
api,web-ssrosse. - Se creó al menos una tabla KVS para la app (la primera tabla activa el servicio).
- El código lee
SUREVA_KVS_URLy un token de tablaSUREVA_KVS_TOKEN_<NAME>desde variables runtime. - Las peticiones están firmadas con SigV4 para
execute-apiusandoSUREVA_KVS_REGION(la región del gateway, no elAWS_REGIONde la app). - El token KVS se envía con
X-KVS-Authorization, no con el headerAuthorizationde producción. - Las respuestas
429respetanRetry-Afterantes de reintentar.