KV Store é um serviço de armazenamento de chave-valor distribuído que opera em toda a rede da Azion. Ele permite que você persista e recupere pequenos pedaços de dados com latência muito baixa de qualquer lugar onde seus usuários estejam, sem precisar gerenciar servidores.

A API JavaScript foi projetada para ser compatível com a API do Cloudflare Workers KV, facilitando a migração de aplicações existentes para a plataforma da Azion.

Casos de uso típicos incluem:

  • Sessões e tokens de autenticação
  • Feature flags e configurações de testes A/B
  • Preferências de usuário e personalização
  • Cache de respostas de API e fragmentos computados
  • Contadores de rate limit e chaves de idempotência
  • Estado do carrinho de compras ou rascunho

Como funciona

O KV Store organiza dados em namespaces, onde cada namespace contém um conjunto independente de chaves. Veja como o sistema opera:

  • Os dados são organizados em namespaces que contêm conjuntos independentes de chaves.
  • Cada item é endereçado por uma chave que é única dentro de seu namespace.
  • Os valores podem ser armazenados como texto, JSON, ArrayBuffer ou ReadableStream.
  • Os dados são replicados entre os pontos de presença globais para maximizar a disponibilidade e o desempenho de leitura.
  • Operações de chave única são atômicas por chave. Transações de múltiplas chaves não são suportadas.

Recursos de implementação

EscopoRecurso
Gerenciar KV Store com FunctionsComo gerenciar KV Store com Functions
Referência da API KV StoreAzion API - KV Store

Resiliência de dados

O KV Store usa uma arquitetura distribuída com replicação entre os nodes da Azion. Novas gravações são aceitas e propagadas para réplicas para garantir durabilidade e alta disponibilidade. Leituras são servidas da réplica saudável mais próxima para minimizar a latência.


Namespaces

Um namespace é um espaço de chaves isolado. Use namespaces para segmentar dados por aplicação, ambiente ou carga de trabalho.

Padrões recomendados

  • Use namespaces separados para ambientes de produção e staging.
  • Prefixe chaves para modelar hierarquia, por exemplo: users/123/profile, flags/new-ui, carts/region-br/user-42.
  • Mantenha as chaves curtas e significativas; prefira alguns segmentos do tipo caminho em vez de longos identificadores opacos.

Convenções de nomenclatura

  • Os nomes devem ser únicos dentro da sua conta.
  • Os nomes devem ter entre 3 e 63 caracteres.
  • Use letras minúsculas, números, hifens e underscores.
  • Os nomes devem corresponder ao padrão: ^[a-zA-Z0-9_-]+$

Gerenciando namespaces via API

Você pode gerenciar namespaces usando a API da Azion. O endpoint base é:

https://api.azion.com/v4/workspace/kv/namespaces

Criar um namespace

Terminal window
curl -X POST "https://api.azion.com/v4/workspace/kv/namespaces" \
-H "Authorization: Token SEU_TOKEN_API" \
-H "Content-Type: application/json" \
-d '{"name": "meu-namespace"}'

Resposta (201 Created):

{
"name": "meu-namespace",
"created_at": "2025-01-24T14:15:22Z",
"last_modified": "2025-01-24T14:15:22Z"
}

Listar namespaces

Terminal window
curl -X GET "https://api.azion.com/v4/workspace/kv/namespaces" \
-H "Authorization: Token SEU_TOKEN_API" \
-H "Accept: application/json"

Parâmetros de consulta:

ParâmetroTipoDescrição
fieldsstringLista separada por vírgulas de nomes de campos para incluir na resposta
pageintegerNúmero da página dentro do conjunto de resultados paginados
page_sizeintegerNúmero de itens por página

Recuperar um namespace

Terminal window
curl -X GET "https://api.azion.com/v4/workspace/kv/namespaces/{namespace}" \
-H "Authorization: Token SEU_TOKEN_API" \
-H "Accept: application/json"

Interação com KV Store via Functions

Você pode interagir com o KV Store diretamente de suas Functions usando a classe Azion.KV. Os exemplos abaixo ilustram padrões comuns para criar, ler, atualizar e excluir dados.

Inicializando o cliente KV

Você pode inicializar o cliente KV usando o construtor ou o método open:

// Usando o namespace padrão
const kv = new Azion.KV();
// Usando um namespace específico
const kv = new Azion.KV("meu-namespace");
// Usando o método open
const kv = await Azion.KV.open("meu-namespace");

Armazenando dados (put)

Armazene valores em diferentes formatos:

async function handleRequest(request) {
const kv = new Azion.KV();
const key = "sessao-usuario";
// Armazenar um valor string
await kv.put(key, "dados-da-sessao-aqui");
// Armazenar um objeto JSON
await kv.put(key, { userId: 123, role: "admin" });
// Armazenar um ArrayBuffer
const encoder = new TextEncoder();
const buffer = encoder.encode("dados binários").buffer;
await kv.put(key, buffer);
// Armazenar com opções (metadados e expiração)
await kv.put(key, "valor", {
metadata: { created_by: "user-42" },
expiration: Math.floor(Date.now() / 1000) + 3600, // expira em 1 hora
expirationTtl: 3600 // alternativa: TTL em segundos
});
return new Response("Dados armazenados", { status: 200 });
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Armazenando dados grandes com streams

Para payloads grandes, use ReadableStream para transmitir dados de forma eficiente:

async function handleRequest(request) {
const kv = new Azion.KV();
const key = "arquivo-grande";
const dataSizeInKB = 500;
const totalSize = dataSizeInKB * 1024;
const chunkSize = 64 * 1024; // chunks de 64KB
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
const numChunks = Math.ceil(totalSize / chunkSize);
for (let i = 0; i < numChunks; i++) {
const currentChunkSize = Math.min(chunkSize, totalSize - i * chunkSize);
const chunk = "a".repeat(currentChunkSize);
controller.enqueue(encoder.encode(chunk));
}
controller.close();
},
});
await kv.put(key, stream);
return new Response("Dados grandes armazenados", { status: 200 });
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Recuperando dados (get)

Recupere valores em diferentes formatos:

async function handleRequest(request) {
const kv = new Azion.KV();
const key = "sessao-usuario";
// Obter como texto (padrão)
const textValue = await kv.get(key, "text");
// Obter como JSON
const jsonValue = await kv.get(key, "json");
console.log(`User ID: ${jsonValue.userId}`);
// Obter como ArrayBuffer
const bufferValue = await kv.get(key, "arrayBuffer");
const decoder = new TextDecoder("utf-8");
const decodedString = decoder.decode(bufferValue);
// Obter como ReadableStream
const streamValue = await kv.get(key, "stream");
// Tratar chaves inexistentes (retorna null)
const missing = await kv.get("chave-inexistente", "text");
if (missing === null) {
console.log("Chave não encontrada");
}
return new Response(textValue, { status: 200 });
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Recuperando múltiplas chaves

Recupere múltiplos valores em uma única operação:

async function handleRequest(request) {
const kv = new Azion.KV();
const keys = ["usuario-1", "usuario-2", "usuario-3"];
const data = await kv.get(keys, "text");
// Retorna um objeto com pares chave-valor
// Chaves inexistentes têm valores null
console.log(JSON.stringify(data));
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" },
status: 200,
});
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Recuperando dados com metadados

Use getWithMetadata para recuperar tanto o valor quanto seus metadados associados:

async function handleRequest(request) {
const kv = new Azion.KV();
const key = "sessao-usuario";
// Armazenar com metadados
await kv.put(key, "valor-sessao", {
metadata: { created_by: "user-42", version: 1 }
});
// Recuperar com metadados
const data = await kv.getWithMetadata(key, "text");
console.log(`Valor: ${data.value}`);
console.log(`Metadados: ${JSON.stringify(data.metadata)}`);
// Funciona com múltiplas chaves também
const keys = ["chave1", "chave2"];
const multiData = await kv.getWithMetadata(keys, "text");
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" },
status: 200,
});
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Lendo streams

Processe dados em stream de forma incremental:

async function handleRequest(request) {
const kv = new Azion.KV();
const key = "arquivo-grande";
const stream = await kv.get(key, "stream");
if (stream instanceof ReadableStream) {
const decoder = new TextDecoder();
let text = "";
for await (const chunk of stream) {
text += decoder.decode(chunk, { stream: true });
}
text += decoder.decode();
console.log(`Recuperados ${text.length} caracteres`);
}
// Ou retorne o stream diretamente na resposta
return new Response(stream);
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Excluindo dados

Remova uma chave do armazenamento:

async function handleRequest(request) {
const kv = new Azion.KV();
const key = "sessao-usuario";
await kv.delete(key);
return new Response("Chave excluída", { status: 200 });
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Trabalhando com Unicode e caracteres especiais

O KV Store suporta chaves e valores codificados em UTF-8 e UTF-16:

async function handleRequest(request) {
const kv = new Azion.KV();
// Chave UTF-16 (Chinês: "Minha Chave")
const key = "我的钥匙";
// Valor UTF-16 com emojis
const value = "🌍🌎🌏 Hello World 你好 世界";
await kv.put(key, value);
const retrieved = await kv.get(key, "text");
console.log(`Chave: ${key}, Valor: ${retrieved}`);
return new Response("OK", { status: 200 });
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Excluindo namespaces

Exclua um namespace inteiro programaticamente:

async function handleRequest(request) {
const namespaceName = "meu-namespace";
// Excluir o namespace
await Azion.KV.delete(namespaceName);
return new Response("Namespace excluído", { status: 200 });
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

Referência de métodos

A classe Azion.KV fornece os seguintes métodos:

Construtor e inicialização

MétodoDescrição
new Azion.KV()Cria uma instância KV usando o namespace padrão
new Azion.KV(name)Cria uma instância KV para o namespace especificado
Azion.KV.open(name)Abre um namespace de forma assíncrona
Azion.KV.delete(name)Exclui um namespace

Operações de dados

MétodoParâmetrosRetornaDescrição
put(key, value, options?)key: string
value: string | object | ArrayBuffer | ReadableStream
options: { metadata?, expiration?, expirationTtl? }
Promise<void>Armazena um valor
get(key, type?, options?)key: string | string[]
type: “text” | “json” | “arrayBuffer” | “stream”
options: { cacheTtl? }
Promise<value | null>Recupera um valor
getWithMetadata(key, type?, options?)key: string | string[]
type: “text” | “json” | “arrayBuffer” | “stream”
options: { cacheTtl? }
Promise<{'{value, metadata}'}>Recupera valor com metadados
delete(key)key: stringPromise<void>Exclui uma chave

Opções do put

OpçãoTipoDescrição
metadataobjectMetadados personalizados para associar à chave (máx 1024 bytes JSON-serializado)
expirationnumberTimestamp Unix (segundos) quando a chave expira
expirationTtlnumberTime-to-live em segundos a partir de agora

Opções do get

OpçãoTipoDescrição
cacheTtlnumberTempo em segundos para armazenar o resultado em cache localmente (mínimo 60 segundos)

Tipos do get

TipoRetornaDescrição
"text"stringRetorna o valor como uma string UTF-8 (padrão)
"json"objectFaz parse do valor como JSON
"arrayBuffer"ArrayBufferRetorna dados binários brutos
"stream"ReadableStreamRetorna um stream legível para valores grandes

Limites

Estes são os limites padrão:

LimiteValor
Taxa de gravação por chaveAté 1 gravação por segundo para a mesma chave
Tamanho da chaveAté 512 bytes (UTF-8)
Tamanho dos metadadosAté 1024 bytes (JSON-serializado)
Tamanho do valorAté 25 MB por item
Comprimento do nome do namespace3-63 caracteres
Mínimo expirationTtl60 segundos
Mínimo cacheTtl60 segundos

Estes são os limites padrão para cada Plano de Serviço:

EscopoDeveloperBusinessEnterpriseMission Critical
Namespaces1000100010001000
Tamanho máximo do arquivo200 MB500 MB2 GB2 GB
Armazenamento máximo por conta5 GB50 GB300 GB300 GB

Limitações

As seguintes operações não são suportadas:

  • Listar chaves: Não existe um método list() para enumerar chaves dentro de um namespace. Projete sua aplicação para rastrear chaves externamente, se necessário.
  • Transações de múltiplas chaves: Operações são atômicas por chave, mas não há suporte para transações que abrangem múltiplas chaves.
  • Gerenciamento de namespaces a partir de functions: Namespaces devem ser criados e gerenciados via API da Azion ou Console, não de dentro de functions.