diff --git a/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/delete.mdx b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/delete.mdx new file mode 100644 index 0000000000..f1d7e8a7bd --- /dev/null +++ b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/delete.mdx @@ -0,0 +1,180 @@ +--- +title: Delete key-value pairs +description: >- + To remove a key-value pair from KV Store, call the delete() method on your + KV instance. +meta_tags: 'kv store, api, functions, delete, remove, storage' +namespace: documentation_runtime_kv_store_delete +permalink: /documentation/runtime/api-reference/kv-store/delete/ +menu_namespace: runtimeMenu +--- + +To remove a key-value pair from KV Store, call the `delete()` method on your +KV instance: + +```javascript +await kv.delete(key); +``` + +## Example + +An example of deleting a key-value pair from within a Function: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + try { + await kv.delete("user-session"); + + return new Response("Key deleted successfully", { status: 200 }); + } catch (e) { + return new Response(e.message, { status: 500 }); + } +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Reference + +### `delete()` method + +To remove a key-value pair, call the `delete()` method on your KV instance: + +```javascript +await kv.delete(key); +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `key` | `string` | The key of the KV pair to delete. | + +#### Response + +| Response | Type | Description | +|----------|------|-------------| +| `response` | `Promise` | A Promise that resolves when the deletion is complete. | + +The `delete()` method returns a Promise that you should `await` to verify +successful deletion. + +--- + +## Guidance + +### Deleting non-existent keys + +Deleting a key that doesn't exist will not throw an error. The operation +completes successfully regardless of whether the key existed: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // This will succeed even if the key doesn't exist + await kv.delete("non-existent-key"); + + return new Response("Delete operation completed", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Eventual consistency + +Like all KV Store operations, deletions are eventually consistent. After +deleting a key: + +- The deletion is immediately visible in the same global network location. +- Other locations may still see the old value for up to 60 seconds (or the + duration of any `cacheTtl` that was set when reading the key). + +### Conditional deletion + +You may want to check if a key exists before deleting it, or perform +additional logic: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "user-session"; + + // Check if key exists before deleting + const value = await kv.get(key, "text"); + + if (value === null) { + return new Response("Key not found", { status: 404 }); + } + + // Perform deletion + await kv.delete(key); + + return new Response("Key deleted", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Deleting multiple keys + +To delete multiple keys, you can use `Promise.all()`: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const keysToDelete = ["key1", "key2", "key3"]; + + await Promise.all(keysToDelete.map(key => kv.delete(key))); + + return new Response("All keys deleted", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Deleting namespaces + +To delete an entire namespace (not just a key), use the static +`Azion.KV.delete()` method: + +```javascript +async function handleRequest(request) { + const namespaceName = "my-namespace"; + + // Delete the entire namespace + await Azion.KV.delete(namespaceName); + + return new Response("Namespace deleted", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +:::caution +Deleting a namespace removes all key-value pairs within it. This operation +cannot be undone. +::: + +--- + +## Related + +- [Write key-value pairs](/en/documentation/runtime/api-reference/kv-store/write/) +- [Read key-value pairs](/en/documentation/runtime/api-reference/kv-store/read/) +- [KV Store API overview](/en/documentation/runtime/api-reference/kv-store/) diff --git a/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/index.mdx b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/index.mdx new file mode 100644 index 0000000000..482b20e4e3 --- /dev/null +++ b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/index.mdx @@ -0,0 +1,86 @@ +--- +title: KV Store API +description: >- + The KV Store API provides methods to store, retrieve, and delete key-value + pairs from within your Functions. +meta_tags: 'kv store, api, functions, key-value, storage' +namespace: documentation_runtime_kv_store_api +permalink: /documentation/runtime/api-reference/kv-store/ +menu_namespace: runtimeMenu +--- + +The KV Store API allows your Functions to interact with Azion's distributed +key-value storage directly from the runtime. This enables low-latency data +persistence and retrieval without external API calls. + +## Initializing the KV client + +You can initialize the KV client using the constructor or the `open` method: + +```javascript +// Using the default namespace +const kv = new Azion.KV(); + +// Using a specific namespace +const kv = new Azion.KV("my-namespace"); + +// Using the open method (async) +const kv = await Azion.KV.open("my-namespace"); +``` + +## Available methods + +The `Azion.KV` class provides the following methods for data operations: + +| Method | Description | Reference | +|--------|-------------|-----------| +| `put(key, value, options?)` | Store a key-value pair | [Write key-value pairs](/en/documentation/runtime/api-reference/kv-store/write/) | +| `get(key, type?, options?)` | Retrieve a value by key | [Read key-value pairs](/en/documentation/runtime/api-reference/kv-store/read/) | +| `getWithMetadata(key, type?, options?)` | Retrieve a value with its metadata | [Read key-value pairs](/en/documentation/runtime/api-reference/kv-store/read/) | +| `delete(key)` | Delete a key-value pair | [Delete key-value pairs](/en/documentation/runtime/api-reference/kv-store/delete/) | + +## Namespace management + +| Method | Description | +|--------|-------------| +| `new Azion.KV()` | Creates a KV instance using the default namespace | +| `new Azion.KV(name)` | Creates a KV instance for the specified namespace | +| `Azion.KV.open(name)` | Opens a namespace asynchronously | +| `Azion.KV.delete(name)` | Deletes a namespace | + +:::note +Namespaces must be created via the [Azion API](https://api.azion.com/) or Console before they can be used in Functions. You cannot create namespaces from within Functions. +::: + +## Supported value types + +KV Store supports the following value types: + +| Type | Description | +|------|-------------| +| `string` | Text data (UTF-8 encoded) | +| `object` | JSON-serializable objects (automatically stringified) | +| `ArrayBuffer` | Binary data | +| `ReadableStream` | Streaming data for large values | + +## Limits + +| Limit | Value | +|-------|-------| +| Key size | Up to 512 bytes (UTF-8) | +| Value size | Up to 25 MB per item | +| Metadata size | Up to 1024 bytes (JSON-serialized) | +| Per-key write rate | Up to 1 write per second to the same key | +| Minimum `expirationTtl` | 60 seconds | +| Minimum `cacheTtl` | 60 seconds | + +## Eventual consistency + +KV Store uses eventual consistency. Writes are immediately visible to other +requests in the same global network location, but can take up to 60 seconds +(or the value of the `cacheTtl` parameter) to be visible in other locations. + +## Related documentation + +- [KV Store reference](/en/documentation/products/store/kv-store/) +- [How to manage KV Store with Functions](/en/documentation/products/guides/kv-store/manage-with-functions/) diff --git a/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/read.mdx b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/read.mdx new file mode 100644 index 0000000000..6d936c4418 --- /dev/null +++ b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/read.mdx @@ -0,0 +1,355 @@ +--- +title: Read key-value pairs +description: >- + To get the value for a given key, call the get() method on your KV instance. +meta_tags: 'kv store, api, functions, read, get, storage' +namespace: documentation_runtime_kv_store_read +permalink: /documentation/runtime/api-reference/kv-store/read/ +menu_namespace: runtimeMenu +--- + +To get the value for a given key, call the `get()` method on your KV instance: + +```javascript +// Read individual key +const value = await kv.get(key); + +// Read multiple keys +const values = await kv.get(keys); +``` + +The `get()` method returns a promise you can `await` to get the value. + +If you request a single key as a string, you will get a single response in the +promise. If the key is not found, the promise will resolve with the literal +value `null`. + +You can also request an array of keys. The return value will be an object with +key-value pairs, where keys not found have `null` values. + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + try { + // Read single key, returns value or null + const value = await kv.get("first-key", "text"); + + // Read multiple keys, returns object with values + const values = await kv.get(["first-key", "second-key"], "text"); + + // Read single key with metadata + const valueWithMetadata = await kv.getWithMetadata("first-key", "text"); + + // Read multiple keys with metadata + const valuesWithMetadata = await kv.getWithMetadata(["first-key", "second-key"], "text"); + + return new Response(JSON.stringify({ + value: value, + values: values, + valueWithMetadata: valueWithMetadata, + valuesWithMetadata: valuesWithMetadata + }), { + headers: { "Content-Type": "application/json" } + }); + } catch (e) { + return new Response(e.message, { status: 500 }); + } +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +:::note +`get()` and `getWithMetadata()` methods may return stale values. If a given +key has recently been read in a given location, writes or updates to the key +made in other locations may take up to 60 seconds (or the duration of the +`cacheTtl`) to display. +::: + +--- + +## Reference + +The following methods are provided to read from KV Store: + +- [get()](#get-method) +- [getWithMetadata()](#getwithmetadata-method) + +### `get()` method + +Use the `get()` method to get a single value, or multiple values if given +multiple keys. + +#### Request a single key + +To get the value for a single key, call the `get()` method on your KV instance: + +```javascript +await kv.get(key, type?, options?); +``` + +##### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `key` | `string` | The key of the KV pair. | +| `type` | `"text"` \| `"json"` \| `"arrayBuffer"` \| `"stream"` | Optional. The type of the value to be returned. `text` is the default. | +| `options` | `{ cacheTtl?: number }` | Optional. Object containing the `cacheTtl` property. | + +##### Options + +| Option | Type | Description | +|--------|------|-------------| +| `cacheTtl` | `number` | Time in seconds to cache the result locally. Minimum value is 60. | + +##### Response + +| Response | Type | Description | +|----------|------|-------------| +| `response` | `Promise` | The value for the requested KV pair, or `null` if not found. | + +The response type depends on the `type` parameter: + +| Type | Returns | +|------|---------| +| `"text"` | A `string` (default) | +| `"json"` | An object decoded from a JSON string | +| `"arrayBuffer"` | An `ArrayBuffer` instance | +| `"stream"` | A `ReadableStream` | + +#### Request multiple keys + +To get the values for multiple keys, call the `get()` method with an array: + +```javascript +await kv.get(keys, type?); +``` + +##### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `keys` | `string[]` | The keys of the KV pairs. | +| `type` | `"text"` \| `"json"` | Optional. The type of the value to be returned. `text` is the default. | + +##### Response + +| Response | Type | Description | +|----------|------|-------------| +| `response` | `Promise` | An object with key-value pairs. Keys not found have `null` values. | + +--- + +### `getWithMetadata()` method + +Use the `getWithMetadata()` method to get a single value along with its +metadata, or multiple values with their metadata. + +#### Request a single key with metadata + +To get the value for a given key along with its metadata: + +```javascript +await kv.getWithMetadata(key, type?, options?); +``` + +Metadata is a serializable value you append to each KV entry when using `put()`. + +##### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `key` | `string` | The key of the KV pair. | +| `type` | `"text"` \| `"json"` \| `"arrayBuffer"` \| `"stream"` | Optional. The type of the value to be returned. `text` is the default. | +| `options` | `{ cacheTtl?: number }` | Optional. Object containing the `cacheTtl` property. | + +##### Response + +| Response | Type | Description | +|----------|------|-------------| +| `response` | `Promise<{ value: string \| object \| ArrayBuffer \| ReadableStream \| null, metadata: object \| null }>` | An object containing the value and metadata. | + +If there is no metadata associated with the requested key-value pair, `null` +will be returned for metadata. + +#### Request multiple keys with metadata + +To get the values for multiple keys along with their metadata: + +```javascript +await kv.getWithMetadata(keys, type?); +``` + +##### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `keys` | `string[]` | The keys of the KV pairs. | +| `type` | `"text"` \| `"json"` | Optional. The type of the value to be returned. `text` is the default. | + +##### Response + +| Response | Type | Description | +|----------|------|-------------| +| `response` | `Promise` | An object with keys mapped to `{ value, metadata }` objects. | + +--- + +## Guidance + +### Type parameter + +For simple values, use the default `text` type which provides your value as a +`string`. For convenience, a `json` type is also specified which will convert +a JSON value into an object before returning it. For large values, use `stream` +to request a `ReadableStream`. For binary values, use `arrayBuffer` to request +an `ArrayBuffer`. + +For large values, the choice of `type` can have a noticeable effect on latency +and CPU usage. For reference, the `type` can be ordered from fastest to slowest +as `stream`, `arrayBuffer`, `text`, and `json`. + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Get as text (default) + const textValue = await kv.get("my-key", "text"); + + // Get as JSON (automatically parsed) + const jsonValue = await kv.get("my-key", "json"); + console.log(jsonValue.someProperty); + + // Get as ArrayBuffer + const bufferValue = await kv.get("my-key", "arrayBuffer"); + const decoder = new TextDecoder("utf-8"); + const decodedString = decoder.decode(bufferValue); + + // Get as ReadableStream + const streamValue = await kv.get("my-key", "stream"); + + return new Response(textValue); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### CacheTtl parameter + +`cacheTtl` is a parameter that defines the length of time in seconds that a KV +result is cached in the global network location where it is accessed. + +Defining the length of time in seconds is useful for reducing cold read latency +on keys that are read relatively infrequently. `cacheTtl` is useful if your +data is write-once or write-rarely. + +:::note[Hot and cold reads] +A hot read means that the data is cached locally. A cold read means that the +data is not cached, so it must be fetched from the central stores. Both +existing key-value pairs and non-existent key-value pairs (negative lookups) +are cached. +::: + +`cacheTtl` is not recommended if your data is updated often and you need to +see updates shortly after they are written, because writes from other global +network locations will not be visible until the cached value expires. + +The minimum value for `cacheTtl` is 60 seconds. + +```javascript +// Cache the result for 5 minutes +const value = await kv.get("config-key", "json", { cacheTtl: 300 }); +``` + +### Handling non-existent keys + +When a key doesn't exist, `get()` returns `null`: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const value = await kv.get("non-existent-key", "text"); + + if (value === null) { + return new Response("Key not found", { status: 404 }); + } + + return new Response(value); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Reading streams + +For large values stored as streams, process the data incrementally: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const stream = await kv.get("large-file", "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(`Retrieved ${text.length} characters`); + } + + // Or return the stream directly in a response + return new Response(stream); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Working with metadata + +Retrieve both value and metadata in a single call: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const result = await kv.getWithMetadata("user-profile", "json"); + + console.log("Value:", result.value); + console.log("Metadata:", result.metadata); + + if (result.metadata && result.metadata.version) { + console.log("Version:", result.metadata.version); + } + + return new Response(JSON.stringify(result), { + headers: { "Content-Type": "application/json" } + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Related + +- [Write key-value pairs](/en/documentation/runtime/api-reference/kv-store/write/) +- [Delete key-value pairs](/en/documentation/runtime/api-reference/kv-store/delete/) +- [KV Store API overview](/en/documentation/runtime/api-reference/kv-store/) diff --git a/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/write.mdx b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/write.mdx new file mode 100644 index 0000000000..eea07ac28a --- /dev/null +++ b/src/content/docs/en/pages/devtools/azion-edge-runtime/api-reference/kv-store/write.mdx @@ -0,0 +1,231 @@ +--- +title: Write key-value pairs +description: >- + To create a new key-value pair, or to update the value for a particular key, + call the put() method on your KV instance. +meta_tags: 'kv store, api, functions, write, put, storage' +namespace: documentation_runtime_kv_store_write +permalink: /documentation/runtime/api-reference/kv-store/write/ +menu_namespace: runtimeMenu +--- + +To create a new key-value pair, or to update the value for a particular key, +call the `put()` method on your KV instance: + +```javascript +await kv.put(key, value); +``` + +## Example + +An example of writing a key-value pair from within a Function: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + try { + await kv.put("first-key", "This is the value for the key"); + + return new Response("Successful write", { status: 201 }); + } catch (e) { + return new Response(e.message, { status: 500 }); + } +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Reference + +### `put()` method + +To create a new key-value pair, or to update the value for a particular key, +call the `put()` method on your KV instance: + +```javascript +await kv.put(key, value, options?); +``` + +#### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `key` | `string` | The key to associate with the value. Keys have a maximum length of 512 bytes. | +| `value` | `string` \| `object` \| `ArrayBuffer` \| `ReadableStream` | The value to store. The type is inferred. The maximum size of a value is 25 MB. | +| `options` | `object` | Optional. An object containing `expiration`, `expirationTtl`, and `metadata` attributes. | + +#### Options + +| Option | Type | Description | +|--------|------|-------------| +| `expiration` | `number` | The number representing when to expire the key-value pair in seconds since epoch. | +| `expirationTtl` | `number` | The number representing when to expire the key-value pair in seconds from now. The minimum value is 60. | +| `metadata` | `object` | An object that must serialize to JSON. The maximum size of the serialized JSON representation is 1024 bytes. | + +#### Response + +| Response | Type | Description | +|----------|------|-------------| +| `response` | `Promise` | A Promise that resolves if the update is successful. | + +The `put()` method returns a Promise that you should `await` to verify a +successful update. + +--- + +## Guidance + +### Writing different value types + +You can store different types of values: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Store a string value + await kv.put("user-name", "John Doe"); + + // Store a JSON object (automatically stringified) + await kv.put("user-data", { id: 123, role: "admin" }); + + // Store an ArrayBuffer + const encoder = new TextEncoder(); + const buffer = encoder.encode("binary data").buffer; + await kv.put("binary-key", buffer); + + // Store a ReadableStream (for large data) + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("large data...")); + controller.close(); + } + }); + await kv.put("stream-key", stream); + + return new Response("Data stored", { status: 201 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Concurrent writes to the same key + +Due to the eventually consistent nature of KV Store, concurrent writes to the +same key can end up overwriting one another. If concurrent writes are made to +the same key, the last write will take precedence. + +Writes are immediately visible to other requests in the same global network +location, but can take up to 60 seconds (or the value of the `cacheTtl` +parameter of the `get()` or `getWithMetadata()` methods) to be visible in +other parts of the world. + +### Expiring keys + +KV Store offers the ability to create keys that automatically expire. You can +configure expiration to occur either at a particular point in time (using the +`expiration` option), or after a certain amount of time has passed since the +key was last modified (using the `expirationTtl` option). + +Once the expiration time of an expiring key is reached, it will be deleted +from the system. After its deletion, attempts to read the key will behave as +if the key does not exist. + +:::note +Expiration targets that are less than 60 seconds into the future are not +supported. This is true for both expiration methods. +::: + +#### Create expiring keys + +To create expiring keys, set `expiration` in the `put()` options to a number +representing the seconds since epoch, or set `expirationTtl` in the `put()` +options to a number representing the seconds from now: + +```javascript +// Expire at a specific time (seconds since epoch) +await kv.put("session-key", "session-data", { + expiration: Math.floor(Date.now() / 1000) + 3600 // 1 hour from now +}); + +// Expire after a duration (TTL in seconds) +await kv.put("cache-key", "cached-data", { + expirationTtl: 300 // 5 minutes from now +}); +``` + +### Metadata + +To associate metadata with a key-value pair, set `metadata` in the `put()` +options to an object (serializable to JSON): + +```javascript +await kv.put("user-profile", JSON.stringify(userData), { + metadata: { + createdBy: "admin", + version: 1, + tags: ["user", "profile"] + } +}); +``` + +You can later retrieve this metadata using the `getWithMetadata()` method. + +### Rate limits + +KV Store has a maximum of 1 write to the same key per second. Writes made to +the same key within 1 second may cause rate limiting errors. + +You should not write more than once per second to the same key. Consider +consolidating your writes to a key within a Function invocation to a single +write, or wait at least 1 second between writes. + +### Storing large data with streams + +For large payloads, use `ReadableStream` to stream data efficiently: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const dataSizeInKB = 500; + const totalSize = dataSizeInKB * 1024; + const chunkSize = 64 * 1024; // 64KB chunks + + 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("large-file", stream); + + return new Response("Large data stored", { status: 201 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Related + +- [Read key-value pairs](/en/documentation/runtime/api-reference/kv-store/read/) +- [Delete key-value pairs](/en/documentation/runtime/api-reference/kv-store/delete/) +- [KV Store API overview](/en/documentation/runtime/api-reference/kv-store/) diff --git a/src/content/docs/en/pages/guides/kv-store/manage-data.mdx b/src/content/docs/en/pages/guides/kv-store/manage-data.mdx new file mode 100644 index 0000000000..a5d2931f52 --- /dev/null +++ b/src/content/docs/en/pages/guides/kv-store/manage-data.mdx @@ -0,0 +1,323 @@ +--- +title: How to manage KV Store with Functions +description: >- + Learn how to store, retrieve, and delete data using KV Store within your + functions. +meta_tags: 'kv store, storage, key-value, functions, data' +namespace: documentation_products_kv_store_manage_edge_functions +permalink: /documentation/products/guides/kv-store/manage-with-functions/ +--- + +import LinkButton from 'azion-webkit/linkbutton' + +This guide shows you how to interact with KV Store from your functions to +store, retrieve, and delete key-value data. + + + +--- + +## Requirements + +Before you begin, make sure you have: + +- [Functions enabled](/en/documentation/products/build/edge-application/edge-functions/) in your application. +- A KV Store namespace created via Console or API. + +--- + +## Creating a function to interact with KV Store + +Follow these steps to create a function that communicates with KV Store: + +1. Access [Azion Console](https://console.azion.com). +2. On the upper-left corner, select **Functions**. +3. Click **+ Function**. +4. Enter a name for your function, for example: `kv-store-handler`. +5. Delete the placeholder code in the editor. +6. Paste one of the code examples below based on your use case. + +--- + +## Storing data + +This example demonstrates how to store different types of data in KV Store: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Store a simple string + await kv.put("greeting", "Hello, World!"); + + // Store a JSON object + await kv.put("user-preferences", { + theme: "dark", + language: "en", + notifications: true + }); + + // Store with metadata and expiration + await kv.put("session-token", "abc123xyz", { + metadata: { userId: "user-42" }, + expirationTtl: 3600 // expires in 1 hour + }); + + return new Response("Data stored successfully", { + headers: { "Content-Type": "text/plain" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Retrieving data + +This example shows how to retrieve data in different formats: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Get as text (default) + const greeting = await kv.get("greeting", "text"); + + // Get as JSON object + const preferences = await kv.get("user-preferences", "json"); + + // Handle missing keys + const missing = await kv.get("non-existent-key", "text"); + if (missing === null) { + console.log("Key not found"); + } + + const response = { + greeting, + preferences, + keyExists: missing !== null + }; + + return new Response(JSON.stringify(response), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Retrieving multiple keys + +Fetch multiple values in a single operation: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Store some test data + await kv.put("user-1", "Alice"); + await kv.put("user-2", "Bob"); + await kv.put("user-3", "Charlie"); + + // Retrieve multiple keys at once + const keys = ["user-1", "user-2", "user-3", "user-4"]; + const users = await kv.get(keys, "text"); + + // Result: { "user-1": "Alice", "user-2": "Bob", "user-3": "Charlie", "user-4": null } + + return new Response(JSON.stringify(users), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Retrieving data with metadata + +Use `getWithMetadata` to retrieve both the value and its associated metadata: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Store data with metadata + await kv.put("session", "active-session-data", { + metadata: { + createdAt: new Date().toISOString(), + userId: "user-42" + } + }); + + // Retrieve with metadata + const result = await kv.getWithMetadata("session", "text"); + + console.log(`Value: ${result.value}`); + console.log(`Metadata: ${JSON.stringify(result.metadata)}`); + + return new Response(JSON.stringify(result), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Deleting data + +Remove a key from the store: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const key = "session-token"; + + // Delete the key + await kv.delete(key); + + return new Response(`Key '${key}' deleted`, { + headers: { "Content-Type": "text/plain" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Working with binary data + +Store and retrieve binary data using ArrayBuffer: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Store binary data + const encoder = new TextEncoder(); + const binaryData = encoder.encode("Binary content here").buffer; + await kv.put("binary-key", binaryData); + + // Retrieve as ArrayBuffer + const retrieved = await kv.get("binary-key", "arrayBuffer"); + + // Decode back to string + const decoder = new TextDecoder("utf-8"); + const decodedString = decoder.decode(retrieved); + + return new Response(decodedString, { + headers: { "Content-Type": "text/plain" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Working with streams + +For large data, use ReadableStream for efficient memory usage: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "large-content"; + + // Retrieve as stream + const stream = await kv.get(key, "stream"); + + if (stream === null) { + return new Response("Key not found", { status: 404 }); + } + + // Return the stream directly in the response + return new Response(stream, { + headers: { "Content-Type": "application/octet-stream" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Using specific namespaces + +Work with different namespaces for data isolation: + +```javascript +async function handleRequest(request) { + // Use a specific namespace + const productionKV = new Azion.KV("production-data"); + const stagingKV = new Azion.KV("staging-data"); + + // Or use the open method + const cacheKV = await Azion.KV.open("cache-namespace"); + + // Each namespace is isolated + await productionKV.put("config", "production-value"); + await stagingKV.put("config", "staging-value"); + + const prodConfig = await productionKV.get("config", "text"); + const stagingConfig = await stagingKV.get("config", "text"); + + return new Response(JSON.stringify({ + production: prodConfig, + staging: stagingConfig + }), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Instantiating the function + +After creating your function, you need to instantiate it in your application: + + + +--- + +## Next steps + +- Learn more about [KV Store](/en/documentation/products/store/kv-store/) +- Explore [Functions](/en/documentation/products/build/edge-application/edge-functions/) + + diff --git a/src/content/docs/en/pages/guides/kv-store/redis-compatibility.mdx b/src/content/docs/en/pages/guides/kv-store/redis-compatibility.mdx new file mode 100644 index 0000000000..67ed0f8179 --- /dev/null +++ b/src/content/docs/en/pages/guides/kv-store/redis-compatibility.mdx @@ -0,0 +1,326 @@ +--- +title: How to use KV Store with Redis-compatible SDK +description: >- + Learn how to use the Azion KV SDK with a Redis-like interface for seamless + integration with your applications. +meta_tags: 'kv store, redis, sdk, key-value, client, compatibility' +namespace: documentation_products_kv_store_redis_compatibility +permalink: /documentation/products/guides/kv-store/redis-compatibility/ +--- + +import LinkButton from 'azion-webkit/linkbutton' + +The Azion KV SDK provides a Redis-like client interface for interacting with +KV Store. This makes it easier to migrate existing applications that use Redis +patterns and provides a familiar developer experience. + + + +--- + +## Requirements + +Before you begin, make sure you have: + +- An [Azion account](https://console.azion.com/signup). +- A KV Store namespace created via Console or API. +- Node.js 18+ or a compatible JavaScript runtime. + +--- + +## Installing the SDK + +Install the Azion SDK using npm: + +```bash +npm install azion +``` + +--- + +## Creating a client + +The SDK uses a Redis-like client pattern with chainable methods: + +```typescript +import { createClient } from 'azion/kv'; + +// Create and connect a client +const client = await createClient() + .on('error', (err) => console.error('KV Client Error:', err)) + .connect(); +``` + +### Client options + +You can customize the client with options: + +```typescript +const client = await createClient({ + namespace: 'my-namespace', + apiToken: 'my-token', +}) + .on('error', (err) => console.error('KV Error:', err)) + .connect(); +``` + +| Option | Type | Description | +|--------|------|-------------| +| `namespace` | string | The KV Store namespace to use | +| `apiToken` | string | Your Azion API token (required for API provider) | + +--- + +## Storing data + +Use the `set` method to store values: + +```typescript +// Simple set +await client.set('user:123', 'John Doe'); + +// Set with expiration (10 seconds) +await client.set('session:abc', 'session-data', { + expiration: { + type: 'EX', + value: 10, + }, +}); + +// Set with metadata +await client.set('config:theme', 'dark', { + metadata: { + updatedBy: 'admin', + version: 1 + }, +}); + +// Set with both expiration and metadata +await client.set('cache:api-response', JSON.stringify(data), { + expiration: { + type: 'EX', + value: 300, // 5 minutes + }, + metadata: { + source: 'external-api', + cached_at: Date.now() + }, +}); +``` + +### Expiration types + +| Type | Description | +|------|-------------| +| `EX` | Expiration time in seconds | +| `PX` | Expiration time in milliseconds | +| `EXAT` | Unix timestamp in seconds | +| `PXAT` | Unix timestamp in milliseconds | + +--- + +## Retrieving data + +Use the `get` method to retrieve values: + +```typescript +// Simple get +const value = await client.get('user:123'); +console.log(value); // 'John Doe' + +// Returns null if key doesn't exist +const missing = await client.get('non-existent'); +console.log(missing); // null +``` + +### Retrieving with metadata + +Use `getWithMetadata` to get both the value and its metadata: + +```typescript +const result = await client.getWithMetadata('config:theme'); +console.log(result.value); // 'dark' +console.log(result.metadata); // { updatedBy: 'admin', version: 1 } +``` + +--- + +## Deleting data + +Use `delete` or `del` to remove keys: + +```typescript +await client.delete('user:123'); +// or +await client.del('user:123'); +``` + +--- + +## Hash operations + +The SDK provides Redis-compatible hash operations for working with field-value pairs: + +### hSet / HSET + +Store a field-value pair: + +```typescript +await client.hSet('user:profile:123', 'name', 'John Doe'); +await client.hSet('user:profile:123', 'email', 'john@example.com'); +await client.hSet('user:profile:123', 'role', 'admin'); + +// Uppercase alias also available +await client.HSET('user:profile:123', 'status', 'active'); +``` + +### hGetAll / HGETALL + +Get all fields and values: + +```typescript +const profile = await client.hGetAll('user:profile:123'); +console.log(profile); +// { name: 'John Doe', email: 'john@example.com', role: 'admin', status: 'active' } + +// Uppercase alias +const data = await client.HGETALL('user:profile:123'); +``` + +### hVals / HVALS + +Get all values (without field names): + +```typescript +const values = await client.hVals('user:profile:123'); +console.log(values); +// ['John Doe', 'john@example.com', 'admin', 'active'] + +// Uppercase alias +const vals = await client.HVALS('user:profile:123'); +``` + +--- + +## Provider detection + +The SDK automatically detects the runtime environment and selects the appropriate provider: + +- **Native provider**: Used when running inside Azion Functions runtime +- **API provider**: Used when running outside Azion (local development, external servers) + +You can check which provider is being used: + +```typescript +const providerType = client.getProviderType(); +console.log(providerType); // 'native' or 'api' +``` + +--- + +## Error handling + +The SDK uses an event-based error handling pattern: + +```typescript +const client = await createClient() + .on('error', (err) => { + console.error('KV Error:', err.message); + // Handle error (retry, fallback, etc.) + }) + .connect(); +``` + +You can also use try-catch for individual operations: + +```typescript +try { + await client.set('key', 'value'); +} catch (error) { + console.error('Failed to set value:', error); +} +``` + +--- + +## Disconnecting + +Always disconnect when you're done: + +```typescript +await client.disconnect(); +// or +await client.quit(); +``` + +--- + +## Complete example + +Here's a complete example showing common operations: + +```typescript +import { createClient } from 'azion/kv'; + +async function main() { + // Create and connect client + const client = await createClient({ + namespace: 'my-app', + }) + .on('error', (err) => console.error('KV Error:', err)) + .connect(); + + try { + // Store user data + await client.set('user:1', JSON.stringify({ name: 'Alice', age: 30 }), { + expiration: { type: 'EX', value: 3600 }, // 1 hour + metadata: { created: Date.now() }, + }); + + // Store user profile using hash operations + await client.hSet('profile:1', 'theme', 'dark'); + await client.hSet('profile:1', 'language', 'en'); + await client.hSet('profile:1', 'notifications', 'true'); + + // Retrieve user data + const userData = await client.get('user:1'); + console.log('User:', JSON.parse(userData)); + + // Retrieve with metadata + const result = await client.getWithMetadata('user:1'); + console.log('Created at:', result.metadata.created); + + // Get all profile settings + const profile = await client.hGetAll('profile:1'); + console.log('Profile:', profile); + + // Clean up + await client.delete('user:1'); + + } finally { + await client.disconnect(); + } +} + +main().catch(console.error); +``` + +--- + +## Redis method mapping + +| Redis Command | SDK Method | Description | +|---------------|------------|-------------| +| `GET` | `get(key)` | Get a value | +| `SET` | `set(key, value, options?)` | Set a value | +| `DEL` | `delete(key)` / `del(key)` | Delete a key | +| `HSET` | `hSet(key, field, value)` / `HSET(...)` | Set hash field | +| `HGETALL` | `hGetAll(key)` / `HGETALL(key)` | Get all hash fields | +| `HVALS` | `hVals(key)` / `HVALS(key)` | Get all hash values | + +--- + +## Next steps + +- Learn more about [KV Store](/en/documentation/products/store/kv-store/) +- Explore [managing KV Store with Functions](/en/documentation/products/guides/kv-store/manage-with-functions/) diff --git a/src/content/docs/en/pages/main-menu/reference/store/kv-store/kv-store.mdx b/src/content/docs/en/pages/main-menu/reference/store/kv-store/kv-store.mdx new file mode 100644 index 0000000000..0d1034f2ef --- /dev/null +++ b/src/content/docs/en/pages/main-menu/reference/store/kv-store/kv-store.mdx @@ -0,0 +1,506 @@ +--- +title: KV Store +description: >- + KV Store is a distributed key-value storage service that runs across Azion's + network, providing low-latency data persistence and retrieval. +meta_tags: 'kv, storage, key, distributed, queries, key-value' +namespace: docs_kv_store +permalink: /documentation/products/store/kv-store/ +--- + +import Tag from 'primevue/tag' +import LinkButton from 'azion-webkit/linkbutton' + + + Preview + + +KV Store is a distributed key-value storage service that runs across Azion's +network. It lets you persist and retrieve small pieces of data with very +low latency from anywhere your users are, without managing servers. + +The JavaScript API is designed to be compatible with the Cloudflare Workers KV +API, making it easier to migrate existing applications to Azion's platform. + +Typical use cases include: + +- Session and authentication tokens +- Feature flags and A/B testing configurations +- User preferences and personalization +- Caching API responses and computed fragments +- Rate-limit counters and idempotency keys +- Shopping cart or draft state + +--- + +## How it works + +KV Store organizes data into namespaces, where each namespace contains an +independent set of keys. Here's how the system operates: + +- Data is organized into **namespaces** that contain independent sets of keys. +- Each item is addressed by a **key** that is unique within its namespace. +- Values can be stored as **text**, **JSON**, **ArrayBuffer**, or **ReadableStream**. +- Data is replicated across Azion nodes to maximize availability and read performance. +- Single-key operations are atomic per key. Multi-key transactions aren't supported. + +--- + +## Implementation resources + +| Scope | Resource | +| ----- | -------- | +| Manage KV Store with Functions | [How to manage KV Store with Functions](/en/documentation/products/guides/kv-store/manage-with-functions/) | +| KV Store API reference | [Azion API - KV Store](https://api.azion.com/) | + +--- + +## Data resilience + +KV Store uses a distributed architecture with replication across Azion +nodes. New writes are accepted and propagated to replicas to ensure +durability and high availability. Reads are served from the closest healthy +replica to minimize latency. + +--- + +## Namespaces + +A namespace is an isolated key space. Use namespaces to segment data by +application, environment, or workload. + +### Recommended patterns + +- Use separate namespaces for production and staging environments. +- Prefix keys to model hierarchy, for example: `users/123/profile`, `flags/new-ui`, `carts/region-br/user-42`. +- Keep keys short and meaningful; prefer a few path-like segments over long opaque identifiers. + +### Naming conventions + +- Names must be unique within your account. +- Names must be between 3 and 63 characters. +- Use lowercase letters, numbers, dashes, and underscores. +- Names must match the pattern: `^[a-zA-Z0-9_-]+$` + +### Managing namespaces via API + +You can manage namespaces using the Azion API. The base endpoint is: + +``` +https://api.azion.com/v4/workspace/kv/namespaces +``` + +#### Create a namespace + +```bash +curl -X POST "https://api.azion.com/v4/workspace/kv/namespaces" \ + -H "Authorization: Token YOUR_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name": "my-namespace"}' +``` + +**Response (201 Created):** + +```json +{ + "name": "my-namespace", + "created_at": "2025-01-24T14:15:22Z", + "last_modified": "2025-01-24T14:15:22Z" +} +``` + +#### List namespaces + +```bash +curl -X GET "https://api.azion.com/v4/workspace/kv/namespaces" \ + -H "Authorization: Token YOUR_API_TOKEN" \ + -H "Accept: application/json" +``` + +**Query parameters:** + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `fields` | string | Comma-separated list of field names to include in the response | +| `page` | integer | Page number within the paginated result set | +| `page_size` | integer | Number of items per page | + +#### Retrieve a namespace + +```bash +curl -X GET "https://api.azion.com/v4/workspace/kv/namespaces/{namespace}" \ + -H "Authorization: Token YOUR_API_TOKEN" \ + -H "Accept: application/json" +``` + +--- + +## Interacting with KV Store via Functions + +You can interact with KV Store directly from your functions using the +`Azion.KV` class. The examples below illustrate common patterns for creating, +reading, updating, and deleting data. + +### Initializing the KV client + +You can initialize the KV client using the constructor or the `open` method: + +```javascript +// Using the default namespace +const kv = new Azion.KV(); + +// Using a specific namespace +const kv = new Azion.KV("my-namespace"); + +// Using the open method +const kv = await Azion.KV.open("my-namespace"); +``` + +### Storing data (put) + +Store values in different formats: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "user-session"; + + // Store a string value + await kv.put(key, "session-data-here"); + + // Store a JSON object + await kv.put(key, { userId: 123, role: "admin" }); + + // Store an ArrayBuffer + const encoder = new TextEncoder(); + const buffer = encoder.encode("binary data").buffer; + await kv.put(key, buffer); + + // Store with options (metadata and expiration) + await kv.put(key, "value", { + metadata: { created_by: "user-42" }, + expiration: Math.floor(Date.now() / 1000) + 3600, // expires in 1 hour + expirationTtl: 3600 // alternative: TTL in seconds + }); + + return new Response("Data stored", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Storing large data with streams + +For large payloads, use `ReadableStream` to stream data efficiently: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "large-file"; + const dataSizeInKB = 500; + const totalSize = dataSizeInKB * 1024; + const chunkSize = 64 * 1024; // 64KB chunks + + 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("Large data stored", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Retrieving data (get) + +Retrieve values in different formats: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "user-session"; + + // Get as text (default) + const textValue = await kv.get(key, "text"); + + // Get as JSON + const jsonValue = await kv.get(key, "json"); + console.log(`User ID: ${jsonValue.userId}`); + + // Get as ArrayBuffer + const bufferValue = await kv.get(key, "arrayBuffer"); + const decoder = new TextDecoder("utf-8"); + const decodedString = decoder.decode(bufferValue); + + // Get as ReadableStream + const streamValue = await kv.get(key, "stream"); + + // Handle non-existent keys (returns null) + const missing = await kv.get("non-existent-key", "text"); + if (missing === null) { + console.log("Key not found"); + } + + return new Response(textValue, { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Retrieving multiple keys + +Retrieve multiple values in a single operation: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const keys = ["user-1", "user-2", "user-3"]; + const data = await kv.get(keys, "text"); + + // Returns an object with key-value pairs + // Non-existent keys have null values + 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)); +}); +``` + +### Retrieving data with metadata + +Use `getWithMetadata` to retrieve both the value and its associated metadata: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "user-session"; + + // Store with metadata + await kv.put(key, "session-value", { + metadata: { created_by: "user-42", version: 1 } + }); + + // Retrieve with metadata + const data = await kv.getWithMetadata(key, "text"); + console.log(`Value: ${data.value}`); + console.log(`Metadata: ${JSON.stringify(data.metadata)}`); + + // Works with multiple keys too + const keys = ["key1", "key2"]; + 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)); +}); +``` + +### Reading streams + +Process streamed data incrementally: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "large-file"; + + 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(`Retrieved ${text.length} characters`); + } + + // Or return the stream directly in a response + return new Response(stream); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Deleting data + +Remove a key from the store: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "user-session"; + + await kv.delete(key); + + return new Response("Key deleted", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Working with Unicode and special characters + +KV Store supports UTF-8 and UTF-16 encoded keys and values: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // UTF-16 key (Chinese: "My Key") + const key = "我的钥匙"; + + // UTF-16 value with emojis + const value = "🌍🌎🌏 Hello World 你好 世界"; + + await kv.put(key, value); + + const retrieved = await kv.get(key, "text"); + console.log(`Key: ${key}, Value: ${retrieved}`); + + return new Response("OK", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Deleting namespaces + +Delete an entire namespace programmatically: + +```javascript +async function handleRequest(request) { + const namespaceName = "my-namespace"; + + // Delete the namespace + await Azion.KV.delete(namespaceName); + + return new Response("Namespace deleted", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Methods reference + +The `Azion.KV` class provides the following methods: + +### Constructor and initialization + +| Method | Description | +| ------ | ----------- | +| `new Azion.KV()` | Creates a KV instance using the default namespace | +| `new Azion.KV(name)` | Creates a KV instance for the specified namespace | +| `Azion.KV.open(name)` | Opens a namespace asynchronously | +| `Azion.KV.delete(name)` | Deletes a namespace | + +### Data operations + +| Method | Parameters | Returns | Description | +| ------ | ---------- | ------- | ----------- | +| `put(key, value, options?)` | `key`: string
`value`: string \| object \| ArrayBuffer \| ReadableStream
`options`: { metadata?, expiration?, expirationTtl? } | `Promise` | Stores a value | +| `get(key, type?, options?)` | `key`: string \| string[]
`type`: "text" \| "json" \| "arrayBuffer" \| "stream"
`options`: { cacheTtl? } | `Promise` | Retrieves a value | +| `getWithMetadata(key, type?, options?)` | `key`: string \| string[]
`type`: "text" \| "json" \| "arrayBuffer" \| "stream"
`options`: { cacheTtl? } | `Promise<{value, metadata}>` | Retrieves value with metadata | +| `delete(key)` | `key`: string | `Promise` | Deletes a key | + +### Put options + +| Option | Type | Description | +| ------ | ---- | ----------- | +| `metadata` | object | Custom metadata to associate with the key (max 1024 bytes JSON-serialized) | +| `expiration` | number | Unix timestamp (seconds) when the key expires | +| `expirationTtl` | number | Time-to-live in seconds from now | + +### Get options + +| Option | Type | Description | +| ------ | ---- | ----------- | +| `cacheTtl` | number | Time in seconds to cache the result locally (minimum 60 seconds) | + +### Get types + +| Type | Returns | Description | +| ---- | ------- | ----------- | +| `"text"` | string | Returns the value as a UTF-8 string (default) | +| `"json"` | object | Parses the value as JSON | +| `"arrayBuffer"` | ArrayBuffer | Returns raw binary data | +| `"stream"` | ReadableStream | Returns a readable stream for large values | + +--- + +## Limits + +These are the **default limits**: + +| Limit | Value | +| ----- | ----- | +| Per-key write rate | Up to 1 write per second to the same key | +| Key size | Up to 512 bytes (UTF-8) | +| Metadata size | Up to 1024 bytes (JSON-serialized) | +| Value size | Up to 25 MB per item | +| Namespace name length | 3-63 characters | +| Minimum `expirationTtl` | 60 seconds | +| Minimum `cacheTtl` | 60 seconds | + +These are the **default limits** for each Service Plan: + +| Scope | Developer | Business | Enterprise | Mission Critical | +| ----- | --------- | -------- | ---------- | ---------------- | +| Namespaces | 1000 | 1000 | 1000 | 1000 | +| Maximum file size | 200 MB | 500 MB | 2 GB | 2 GB | +| Maximum storage per account | 5 GB | 50 GB | 300 GB | 300 GB | + +--- + +## Limitations + +The following operations are **not supported**: + +- **List keys**: There is no `list()` method to enumerate keys within a namespace. Design your application to track keys externally if needed. +- **Multi-key transactions**: Operations are atomic per key, but there is no support for transactions spanning multiple keys. +- **Namespace management from functions**: Namespaces must be created and managed via the [Azion API](https://api.azion.com/) or Console, not from within functions. + +--- diff --git a/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/delete.mdx b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/delete.mdx new file mode 100644 index 0000000000..78a79a8990 --- /dev/null +++ b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/delete.mdx @@ -0,0 +1,180 @@ +--- +title: Excluir pares chave-valor +description: >- + Para remover um par chave-valor do KV Store, chame o método delete() na sua + instância KV. +meta_tags: 'kv store, api, functions, excluir, delete, armazenamento' +namespace: documentation_runtime_kv_store_delete +permalink: /documentacao/runtime/api-reference/kv-store/delete/ +menu_namespace: runtimeMenu +--- + +Para remover um par chave-valor do KV Store, chame o método `delete()` na sua +instância KV: + +```javascript +await kv.delete(key); +``` + +## Exemplo + +Um exemplo de exclusão de um par chave-valor de dentro de uma Function: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + try { + await kv.delete("sessao-usuario"); + + return new Response("Chave excluída com sucesso", { status: 200 }); + } catch (e) { + return new Response(e.message, { status: 500 }); + } +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Referência + +### Método `delete()` + +Para remover um par chave-valor, chame o método `delete()` na sua instância KV: + +```javascript +await kv.delete(key); +``` + +#### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `key` | `string` | A chave do par KV a ser excluído. | + +#### Resposta + +| Resposta | Tipo | Descrição | +|----------|------|-----------| +| `response` | `Promise` | Uma Promise que resolve quando a exclusão é concluída. | + +O método `delete()` retorna uma Promise que você deve usar `await` para +verificar a exclusão bem-sucedida. + +--- + +## Orientações + +### Excluindo chaves inexistentes + +Excluir uma chave que não existe não lançará um erro. A operação é concluída +com sucesso independentemente de a chave existir: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Isso terá sucesso mesmo se a chave não existir + await kv.delete("chave-inexistente"); + + return new Response("Operação de exclusão concluída", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Consistência eventual + +Como todas as operações do KV Store, exclusões são eventualmente consistentes. +Após excluir uma chave: + +- A exclusão é imediatamente visível na mesma localização da rede global. +- Outras localizações ainda podem ver o valor antigo por até 60 segundos (ou a + duração de qualquer `cacheTtl` que foi definido ao ler a chave). + +### Exclusão condicional + +Você pode querer verificar se uma chave existe antes de excluí-la, ou executar +lógica adicional: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const key = "sessao-usuario"; + + // Verificar se a chave existe antes de excluir + const value = await kv.get(key, "text"); + + if (value === null) { + return new Response("Chave não encontrada", { status: 404 }); + } + + // Executar exclusão + await kv.delete(key); + + return new Response("Chave excluída", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Excluindo múltiplas chaves + +Para excluir múltiplas chaves, você pode usar `Promise.all()`: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const chavesParaExcluir = ["chave1", "chave2", "chave3"]; + + await Promise.all(chavesParaExcluir.map(key => kv.delete(key))); + + return new Response("Todas as chaves excluídas", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Excluindo namespaces + +Para excluir um namespace inteiro (não apenas uma chave), use o método estático +`Azion.KV.delete()`: + +```javascript +async function handleRequest(request) { + const namespaceName = "meu-namespace"; + + // Excluir o namespace inteiro + await Azion.KV.delete(namespaceName); + + return new Response("Namespace excluído", { status: 200 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +:::caution +Excluir um namespace remove todos os pares chave-valor dentro dele. Esta +operação não pode ser desfeita. +::: + +--- + +## Relacionados + +- [Escrever pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/write/) +- [Ler pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/read/) +- [Visão geral da API KV Store](/pt-br/documentacao/runtime/api-reference/kv-store/) diff --git a/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/index.mdx b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/index.mdx new file mode 100644 index 0000000000..b7c876615a --- /dev/null +++ b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/index.mdx @@ -0,0 +1,87 @@ +--- +title: API KV Store +description: >- + A API KV Store fornece métodos para armazenar, recuperar e excluir pares + chave-valor de dentro de suas Functions. +meta_tags: 'kv store, api, functions, chave-valor, armazenamento' +namespace: documentation_runtime_kv_store_api +permalink: /documentacao/runtime/api-reference/kv-store/ +menu_namespace: runtimeMenu +--- + +A API KV Store permite que suas Functions interajam com o armazenamento +chave-valor distribuído da Azion diretamente do runtime. Isso possibilita +persistência e recuperação de dados com baixa latência sem chamadas de API externas. + +## Inicializando o cliente KV + +Você pode inicializar o cliente KV usando o construtor ou o método `open`: + +```javascript +// 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 (assíncrono) +const kv = await Azion.KV.open("meu-namespace"); +``` + +## Métodos disponíveis + +A classe `Azion.KV` fornece os seguintes métodos para operações de dados: + +| Método | Descrição | Referência | +|--------|-----------|------------| +| `put(key, value, options?)` | Armazena um par chave-valor | [Escrever pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/write/) | +| `get(key, type?, options?)` | Recupera um valor pela chave | [Ler pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/read/) | +| `getWithMetadata(key, type?, options?)` | Recupera um valor com seus metadados | [Ler pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/read/) | +| `delete(key)` | Exclui um par chave-valor | [Excluir pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/delete/) | + +## Gerenciamento de namespaces + +| Método | Descriçã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 | + +:::note +Namespaces devem ser criados via [API da Azion](https://api.azion.com/) ou Console antes de poderem ser usados em Functions. Você não pode criar namespaces de dentro de Functions. +::: + +## Tipos de valores suportados + +O KV Store suporta os seguintes tipos de valores: + +| Tipo | Descrição | +|------|-----------| +| `string` | Dados de texto (codificados em UTF-8) | +| `object` | Objetos serializáveis em JSON (automaticamente convertidos para string) | +| `ArrayBuffer` | Dados binários | +| `ReadableStream` | Dados em streaming para valores grandes | + +## Limites + +| Limite | Valor | +|--------|-------| +| Tamanho da chave | Até 512 bytes (UTF-8) | +| Tamanho do valor | Até 25 MB por item | +| Tamanho dos metadados | Até 1024 bytes (JSON-serializado) | +| Taxa de gravação por chave | Até 1 gravação por segundo para a mesma chave | +| Mínimo `expirationTtl` | 60 segundos | +| Mínimo `cacheTtl` | 60 segundos | + +## Consistência eventual + +O KV Store usa consistência eventual. Gravações são imediatamente visíveis para +outras requisições na mesma localização da rede global, mas podem levar até 60 +segundos (ou o valor do parâmetro `cacheTtl`) para serem visíveis em outras +localizações. + +## Documentação relacionada + +- [Referência do KV Store](/pt-br/documentacao/produtos/store/kv-store/) +- [Como gerenciar KV Store com Functions](/pt-br/documentacao/produtos/guias/kv-store/gerenciar-com-funcoes/) diff --git a/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/read.mdx b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/read.mdx new file mode 100644 index 0000000000..5e8f71ed27 --- /dev/null +++ b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/read.mdx @@ -0,0 +1,359 @@ +--- +title: Ler pares chave-valor +description: >- + Para obter o valor de uma chave específica, chame o método get() na sua + instância KV. +meta_tags: 'kv store, api, functions, ler, get, armazenamento' +namespace: documentation_runtime_kv_store_read +permalink: /documentacao/runtime/api-reference/kv-store/read/ +menu_namespace: runtimeMenu +--- + +Para obter o valor de uma chave específica, chame o método `get()` na sua +instância KV: + +```javascript +// Ler chave individual +const value = await kv.get(key); + +// Ler múltiplas chaves +const values = await kv.get(keys); +``` + +O método `get()` retorna uma promise que você pode usar `await` para obter o valor. + +Se você solicitar uma única chave como string, receberá uma única resposta na +promise. Se a chave não for encontrada, a promise resolverá com o valor literal +`null`. + +Você também pode solicitar um array de chaves. O valor de retorno será um +objeto com pares chave-valor, onde chaves não encontradas têm valores `null`. + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + try { + // Ler chave única, retorna valor ou null + const value = await kv.get("primeira-chave", "text"); + + // Ler múltiplas chaves, retorna objeto com valores + const values = await kv.get(["primeira-chave", "segunda-chave"], "text"); + + // Ler chave única com metadados + const valueWithMetadata = await kv.getWithMetadata("primeira-chave", "text"); + + // Ler múltiplas chaves com metadados + const valuesWithMetadata = await kv.getWithMetadata(["primeira-chave", "segunda-chave"], "text"); + + return new Response(JSON.stringify({ + value: value, + values: values, + valueWithMetadata: valueWithMetadata, + valuesWithMetadata: valuesWithMetadata + }), { + headers: { "Content-Type": "application/json" } + }); + } catch (e) { + return new Response(e.message, { status: 500 }); + } +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +:::note +Os métodos `get()` e `getWithMetadata()` podem retornar valores desatualizados. +Se uma chave foi lida recentemente em uma determinada localização, gravações ou +atualizações na chave feitas em outras localizações podem levar até 60 segundos +(ou a duração do `cacheTtl`) para serem exibidas. +::: + +--- + +## Referência + +Os seguintes métodos são fornecidos para ler do KV Store: + +- [get()](#método-get) +- [getWithMetadata()](#método-getwithmetadata) + +### Método `get()` + +Use o método `get()` para obter um único valor, ou múltiplos valores se forem +fornecidas múltiplas chaves. + +#### Solicitar uma única chave + +Para obter o valor de uma única chave, chame o método `get()` na sua instância KV: + +```javascript +await kv.get(key, type?, options?); +``` + +##### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `key` | `string` | A chave do par KV. | +| `type` | `"text"` \| `"json"` \| `"arrayBuffer"` \| `"stream"` | Opcional. O tipo do valor a ser retornado. `text` é o padrão. | +| `options` | `{ cacheTtl?: number }` | Opcional. Objeto contendo a propriedade `cacheTtl`. | + +##### Opções + +| Opção | Tipo | Descrição | +|-------|------|-----------| +| `cacheTtl` | `number` | Tempo em segundos para armazenar o resultado em cache localmente. O valor mínimo é 60. | + +##### Resposta + +| Resposta | Tipo | Descrição | +|----------|------|-----------| +| `response` | `Promise` | O valor para o par KV solicitado, ou `null` se não encontrado. | + +O tipo de resposta depende do parâmetro `type`: + +| Tipo | Retorna | +|------|---------| +| `"text"` | Uma `string` (padrão) | +| `"json"` | Um objeto decodificado de uma string JSON | +| `"arrayBuffer"` | Uma instância de `ArrayBuffer` | +| `"stream"` | Um `ReadableStream` | + +#### Solicitar múltiplas chaves + +Para obter os valores de múltiplas chaves, chame o método `get()` com um array: + +```javascript +await kv.get(keys, type?); +``` + +##### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `keys` | `string[]` | As chaves dos pares KV. | +| `type` | `"text"` \| `"json"` | Opcional. O tipo do valor a ser retornado. `text` é o padrão. | + +##### Resposta + +| Resposta | Tipo | Descrição | +|----------|------|-----------| +| `response` | `Promise` | Um objeto com pares chave-valor. Chaves não encontradas têm valores `null`. | + +--- + +### Método `getWithMetadata()` + +Use o método `getWithMetadata()` para obter um único valor junto com seus +metadados, ou múltiplos valores com seus metadados. + +#### Solicitar uma única chave com metadados + +Para obter o valor de uma chave junto com seus metadados: + +```javascript +await kv.getWithMetadata(key, type?, options?); +``` + +Metadados são um valor serializável que você anexa a cada entrada KV ao usar `put()`. + +##### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `key` | `string` | A chave do par KV. | +| `type` | `"text"` \| `"json"` \| `"arrayBuffer"` \| `"stream"` | Opcional. O tipo do valor a ser retornado. `text` é o padrão. | +| `options` | `{ cacheTtl?: number }` | Opcional. Objeto contendo a propriedade `cacheTtl`. | + +##### Resposta + +| Resposta | Tipo | Descrição | +|----------|------|-----------| +| `response` | `Promise<{ value: string \| object \| ArrayBuffer \| ReadableStream \| null, metadata: object \| null }>` | Um objeto contendo o valor e os metadados. | + +Se não houver metadados associados ao par chave-valor solicitado, `null` será +retornado para metadata. + +#### Solicitar múltiplas chaves com metadados + +Para obter os valores de múltiplas chaves junto com seus metadados: + +```javascript +await kv.getWithMetadata(keys, type?); +``` + +##### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `keys` | `string[]` | As chaves dos pares KV. | +| `type` | `"text"` \| `"json"` | Opcional. O tipo do valor a ser retornado. `text` é o padrão. | + +##### Resposta + +| Resposta | Tipo | Descrição | +|----------|------|-----------| +| `response` | `Promise` | Um objeto com chaves mapeadas para objetos `{ value, metadata }`. | + +--- + +## Orientações + +### Parâmetro type + +Para valores simples, use o tipo padrão `text` que fornece seu valor como uma +`string`. Por conveniência, um tipo `json` também é especificado que converterá +um valor JSON em um objeto antes de retorná-lo. Para valores grandes, use +`stream` para solicitar um `ReadableStream`. Para valores binários, use +`arrayBuffer` para solicitar um `ArrayBuffer`. + +Para valores grandes, a escolha do `type` pode ter um efeito notável na latência +e uso de CPU. Para referência, o `type` pode ser ordenado do mais rápido ao mais +lento como `stream`, `arrayBuffer`, `text` e `json`. + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Obter como texto (padrão) + const textValue = await kv.get("minha-chave", "text"); + + // Obter como JSON (automaticamente parseado) + const jsonValue = await kv.get("minha-chave", "json"); + console.log(jsonValue.someProperty); + + // Obter como ArrayBuffer + const bufferValue = await kv.get("minha-chave", "arrayBuffer"); + const decoder = new TextDecoder("utf-8"); + const decodedString = decoder.decode(bufferValue); + + // Obter como ReadableStream + const streamValue = await kv.get("minha-chave", "stream"); + + return new Response(textValue); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Parâmetro cacheTtl + +`cacheTtl` é um parâmetro que define o tempo em segundos que um resultado KV +é armazenado em cache na localização da rede global onde é acessado. + +Definir o tempo em segundos é útil para reduzir a latência de leitura fria em +chaves que são lidas com pouca frequência. `cacheTtl` é útil se seus dados são +escritos uma vez ou raramente. + +:::note[Leituras quentes e frias] +Uma leitura quente significa que os dados estão em cache localmente. Uma +leitura fria significa que os dados não estão em cache, então devem ser +buscados dos armazenamentos centrais. Tanto pares chave-valor existentes +quanto pares chave-valor inexistentes (lookups negativos) são armazenados +em cache. +::: + +`cacheTtl` não é recomendado se seus dados são atualizados frequentemente e +você precisa ver as atualizações logo após serem escritas, porque gravações +de outras localizações da rede global não serão visíveis até que o valor em +cache expire. + +O valor mínimo para `cacheTtl` é 60 segundos. + +```javascript +// Armazenar o resultado em cache por 5 minutos +const value = await kv.get("chave-config", "json", { cacheTtl: 300 }); +``` + +### Tratando chaves inexistentes + +Quando uma chave não existe, `get()` retorna `null`: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const value = await kv.get("chave-inexistente", "text"); + + if (value === null) { + return new Response("Chave não encontrada", { status: 404 }); + } + + return new Response(value); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Lendo streams + +Para valores grandes armazenados como streams, processe os dados incrementalmente: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const stream = await kv.get("arquivo-grande", "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)); +}); +``` + +### Trabalhando com metadados + +Recupere tanto o valor quanto os metadados em uma única chamada: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const result = await kv.getWithMetadata("perfil-usuario", "json"); + + console.log("Valor:", result.value); + console.log("Metadados:", result.metadata); + + if (result.metadata && result.metadata.version) { + console.log("Versão:", result.metadata.version); + } + + return new Response(JSON.stringify(result), { + headers: { "Content-Type": "application/json" } + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Relacionados + +- [Escrever pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/write/) +- [Excluir pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/delete/) +- [Visão geral da API KV Store](/pt-br/documentacao/runtime/api-reference/kv-store/) diff --git a/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/write.mdx b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/write.mdx new file mode 100644 index 0000000000..b5dd6e5f19 --- /dev/null +++ b/src/content/docs/pt-br/pages/devtools/azion-edge-runtime/api-reference/kv-store/write.mdx @@ -0,0 +1,231 @@ +--- +title: Escrever pares chave-valor +description: >- + Para criar um novo par chave-valor, ou atualizar o valor de uma chave + específica, chame o método put() na sua instância KV. +meta_tags: 'kv store, api, functions, escrever, put, armazenamento' +namespace: documentation_runtime_kv_store_write +permalink: /documentacao/runtime/api-reference/kv-store/write/ +menu_namespace: runtimeMenu +--- + +Para criar um novo par chave-valor, ou atualizar o valor de uma chave +específica, chame o método `put()` na sua instância KV: + +```javascript +await kv.put(key, value); +``` + +## Exemplo + +Um exemplo de escrita de um par chave-valor de dentro de uma Function: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + try { + await kv.put("primeira-chave", "Este é o valor para a chave"); + + return new Response("Escrita bem-sucedida", { status: 201 }); + } catch (e) { + return new Response(e.message, { status: 500 }); + } +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Referência + +### Método `put()` + +Para criar um novo par chave-valor, ou atualizar o valor de uma chave +específica, chame o método `put()` na sua instância KV: + +```javascript +await kv.put(key, value, options?); +``` + +#### Parâmetros + +| Parâmetro | Tipo | Descrição | +|-----------|------|-----------| +| `key` | `string` | A chave para associar ao valor. Chaves têm um comprimento máximo de 512 bytes. | +| `value` | `string` \| `object` \| `ArrayBuffer` \| `ReadableStream` | O valor a ser armazenado. O tipo é inferido. O tamanho máximo de um valor é 25 MB. | +| `options` | `object` | Opcional. Um objeto contendo os atributos `expiration`, `expirationTtl` e `metadata`. | + +#### Opções + +| Opção | Tipo | Descrição | +|-------|------|-----------| +| `expiration` | `number` | O número representando quando expirar o par chave-valor em segundos desde epoch. | +| `expirationTtl` | `number` | O número representando quando expirar o par chave-valor em segundos a partir de agora. O valor mínimo é 60. | +| `metadata` | `object` | Um objeto que deve ser serializável em JSON. O tamanho máximo da representação JSON serializada é 1024 bytes. | + +#### Resposta + +| Resposta | Tipo | Descrição | +|----------|------|-----------| +| `response` | `Promise` | Uma Promise que resolve se a atualização for bem-sucedida. | + +O método `put()` retorna uma Promise que você deve usar `await` para verificar +uma atualização bem-sucedida. + +--- + +## Orientações + +### Escrevendo diferentes tipos de valores + +Você pode armazenar diferentes tipos de valores: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Armazenar um valor string + await kv.put("nome-usuario", "John Doe"); + + // Armazenar um objeto JSON (automaticamente convertido para string) + await kv.put("dados-usuario", { id: 123, role: "admin" }); + + // Armazenar um ArrayBuffer + const encoder = new TextEncoder(); + const buffer = encoder.encode("dados binários").buffer; + await kv.put("chave-binaria", buffer); + + // Armazenar um ReadableStream (para dados grandes) + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("dados grandes...")); + controller.close(); + } + }); + await kv.put("chave-stream", stream); + + return new Response("Dados armazenados", { status: 201 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +### Gravações concorrentes na mesma chave + +Devido à natureza eventualmente consistente do KV Store, gravações concorrentes +na mesma chave podem acabar sobrescrevendo umas às outras. Se gravações +concorrentes forem feitas na mesma chave, a última gravação terá precedência. + +Gravações são imediatamente visíveis para outras requisições na mesma +localização da rede global, mas podem levar até 60 segundos (ou o valor do +parâmetro `cacheTtl` dos métodos `get()` ou `getWithMetadata()`) para serem +visíveis em outras partes do mundo. + +### Chaves com expiração + +O KV Store oferece a capacidade de criar chaves que expiram automaticamente. +Você pode configurar a expiração para ocorrer em um momento específico (usando +a opção `expiration`), ou após um certo período de tempo desde que a chave foi +modificada pela última vez (usando a opção `expirationTtl`). + +Uma vez que o tempo de expiração de uma chave é atingido, ela será excluída do +sistema. Após sua exclusão, tentativas de ler a chave se comportarão como se a +chave não existisse. + +:::note +Alvos de expiração que são menores que 60 segundos no futuro não são +suportados. Isso é verdade para ambos os métodos de expiração. +::: + +#### Criar chaves com expiração + +Para criar chaves com expiração, defina `expiration` nas opções do `put()` como +um número representando os segundos desde epoch, ou defina `expirationTtl` nas +opções do `put()` como um número representando os segundos a partir de agora: + +```javascript +// Expirar em um momento específico (segundos desde epoch) +await kv.put("chave-sessao", "dados-sessao", { + expiration: Math.floor(Date.now() / 1000) + 3600 // 1 hora a partir de agora +}); + +// Expirar após uma duração (TTL em segundos) +await kv.put("chave-cache", "dados-cache", { + expirationTtl: 300 // 5 minutos a partir de agora +}); +``` + +### Metadados + +Para associar metadados a um par chave-valor, defina `metadata` nas opções do +`put()` como um objeto (serializável em JSON): + +```javascript +await kv.put("perfil-usuario", JSON.stringify(dadosUsuario), { + metadata: { + createdBy: "admin", + version: 1, + tags: ["usuario", "perfil"] + } +}); +``` + +Você pode recuperar esses metadados posteriormente usando o método `getWithMetadata()`. + +### Limites de taxa + +O KV Store tem um máximo de 1 gravação na mesma chave por segundo. Gravações +feitas na mesma chave dentro de 1 segundo podem causar erros de limitação de taxa. + +Você não deve gravar mais de uma vez por segundo na mesma chave. Considere +consolidar suas gravações em uma chave dentro de uma invocação de Function em +uma única gravação, ou aguarde pelo menos 1 segundo entre gravações. + +### Armazenando dados grandes com streams + +Para payloads grandes, use `ReadableStream` para transmitir dados de forma eficiente: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + 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("arquivo-grande", stream); + + return new Response("Dados grandes armazenados", { status: 201 }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Relacionados + +- [Ler pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/read/) +- [Excluir pares chave-valor](/pt-br/documentacao/runtime/api-reference/kv-store/delete/) +- [Visão geral da API KV Store](/pt-br/documentacao/runtime/api-reference/kv-store/) diff --git a/src/content/docs/pt-br/pages/guias/kv-store/compatibilidade-redis.mdx b/src/content/docs/pt-br/pages/guias/kv-store/compatibilidade-redis.mdx new file mode 100644 index 0000000000..cad266e639 --- /dev/null +++ b/src/content/docs/pt-br/pages/guias/kv-store/compatibilidade-redis.mdx @@ -0,0 +1,326 @@ +--- +title: Como usar KV Store com SDK compatível com Redis +description: >- + Aprenda como usar o SDK Azion KV com uma interface similar ao Redis para + integração simplificada com suas aplicações. +meta_tags: 'kv store, redis, sdk, chave-valor, cliente, compatibilidade' +namespace: documentation_products_kv_store_redis_compatibility +permalink: /documentacao/produtos/guias/kv-store/compatibilidade-redis/ +--- + +import LinkButton from 'azion-webkit/linkbutton' + +O SDK Azion KV fornece uma interface de cliente similar ao Redis para interagir +com o KV Store. Isso facilita a migração de aplicações existentes que usam +padrões Redis e proporciona uma experiência familiar para desenvolvedores. + + + +--- + +## Requisitos + +Antes de começar, certifique-se de ter: + +- Uma [conta Azion](https://console.azion.com/signup). +- Um namespace do KV Store criado via Console ou API. +- Node.js 18+ ou um runtime JavaScript compatível. + +--- + +## Instalando o SDK + +Instale o SDK da Azion usando npm: + +```bash +npm install azion +``` + +--- + +## Criando um cliente + +O SDK usa um padrão de cliente similar ao Redis com métodos encadeáveis: + +```typescript +import { createClient } from 'azion/kv'; + +// Criar e conectar um cliente +const client = await createClient() + .on('error', (err) => console.error('Erro do Cliente KV:', err)) + .connect(); +``` + +### Opções do cliente + +Você pode personalizar o cliente com opções: + +```typescript +const client = await createClient({ + namespace: 'meu-namespace', + apiToken: 'meu-token', +}) + .on('error', (err) => console.error('Erro KV:', err)) + .connect(); +``` + +| Opção | Tipo | Descrição | +|-------|------|-----------| +| `namespace` | string | O namespace do KV Store a ser usado | +| `apiToken` | string | Seu token da API Azion (obrigatório para provider API) | + +--- + +## Armazenando dados + +Use o método `set` para armazenar valores: + +```typescript +// Set simples +await client.set('user:123', 'John Doe'); + +// Set com expiração (10 segundos) +await client.set('session:abc', 'dados-sessao', { + expiration: { + type: 'EX', + value: 10, + }, +}); + +// Set com metadados +await client.set('config:theme', 'dark', { + metadata: { + updatedBy: 'admin', + version: 1 + }, +}); + +// Set com expiração e metadados +await client.set('cache:api-response', JSON.stringify(data), { + expiration: { + type: 'EX', + value: 300, // 5 minutos + }, + metadata: { + source: 'external-api', + cached_at: Date.now() + }, +}); +``` + +### Tipos de expiração + +| Tipo | Descrição | +|------|-----------| +| `EX` | Tempo de expiração em segundos | +| `PX` | Tempo de expiração em milissegundos | +| `EXAT` | Timestamp Unix em segundos | +| `PXAT` | Timestamp Unix em milissegundos | + +--- + +## Recuperando dados + +Use o método `get` para recuperar valores: + +```typescript +// Get simples +const value = await client.get('user:123'); +console.log(value); // 'John Doe' + +// Retorna null se a chave não existir +const missing = await client.get('inexistente'); +console.log(missing); // null +``` + +### Recuperando com metadados + +Use `getWithMetadata` para obter tanto o valor quanto seus metadados: + +```typescript +const result = await client.getWithMetadata('config:theme'); +console.log(result.value); // 'dark' +console.log(result.metadata); // { updatedBy: 'admin', version: 1 } +``` + +--- + +## Excluindo dados + +Use `delete` ou `del` para remover chaves: + +```typescript +await client.delete('user:123'); +// ou +await client.del('user:123'); +``` + +--- + +## Operações de hash + +O SDK fornece operações de hash compatíveis com Redis para trabalhar com pares campo-valor: + +### hSet / HSET + +Armazena um par campo-valor: + +```typescript +await client.hSet('user:profile:123', 'name', 'John Doe'); +await client.hSet('user:profile:123', 'email', 'john@example.com'); +await client.hSet('user:profile:123', 'role', 'admin'); + +// Alias em maiúsculas também disponível +await client.HSET('user:profile:123', 'status', 'active'); +``` + +### hGetAll / HGETALL + +Obtém todos os campos e valores: + +```typescript +const profile = await client.hGetAll('user:profile:123'); +console.log(profile); +// { name: 'John Doe', email: 'john@example.com', role: 'admin', status: 'active' } + +// Alias em maiúsculas +const data = await client.HGETALL('user:profile:123'); +``` + +### hVals / HVALS + +Obtém todos os valores (sem os nomes dos campos): + +```typescript +const values = await client.hVals('user:profile:123'); +console.log(values); +// ['John Doe', 'john@example.com', 'admin', 'active'] + +// Alias em maiúsculas +const vals = await client.HVALS('user:profile:123'); +``` + +--- + +## Detecção de provider + +O SDK detecta automaticamente o ambiente de execução e seleciona o provider apropriado: + +- **Provider nativo**: Usado quando executando dentro do runtime de Functions da Azion +- **Provider API**: Usado quando executando fora da Azion (desenvolvimento local, servidores externos) + +Você pode verificar qual provider está sendo usado: + +```typescript +const providerType = client.getProviderType(); +console.log(providerType); // 'native' ou 'api' +``` + +--- + +## Tratamento de erros + +O SDK usa um padrão de tratamento de erros baseado em eventos: + +```typescript +const client = await createClient() + .on('error', (err) => { + console.error('Erro KV:', err.message); + // Tratar erro (retry, fallback, etc.) + }) + .connect(); +``` + +Você também pode usar try-catch para operações individuais: + +```typescript +try { + await client.set('key', 'value'); +} catch (error) { + console.error('Falha ao definir valor:', error); +} +``` + +--- + +## Desconectando + +Sempre desconecte quando terminar: + +```typescript +await client.disconnect(); +// ou +await client.quit(); +``` + +--- + +## Exemplo completo + +Aqui está um exemplo completo mostrando operações comuns: + +```typescript +import { createClient } from 'azion/kv'; + +async function main() { + // Criar e conectar cliente + const client = await createClient({ + namespace: 'minha-app', + }) + .on('error', (err) => console.error('Erro KV:', err)) + .connect(); + + try { + // Armazenar dados do usuário + await client.set('user:1', JSON.stringify({ name: 'Alice', age: 30 }), { + expiration: { type: 'EX', value: 3600 }, // 1 hora + metadata: { created: Date.now() }, + }); + + // Armazenar perfil do usuário usando operações de hash + await client.hSet('profile:1', 'theme', 'dark'); + await client.hSet('profile:1', 'language', 'pt-br'); + await client.hSet('profile:1', 'notifications', 'true'); + + // Recuperar dados do usuário + const userData = await client.get('user:1'); + console.log('Usuário:', JSON.parse(userData)); + + // Recuperar com metadados + const result = await client.getWithMetadata('user:1'); + console.log('Criado em:', result.metadata.created); + + // Obter todas as configurações do perfil + const profile = await client.hGetAll('profile:1'); + console.log('Perfil:', profile); + + // Limpar + await client.delete('user:1'); + + } finally { + await client.disconnect(); + } +} + +main().catch(console.error); +``` + +--- + +## Mapeamento de métodos Redis + +| Comando Redis | Método SDK | Descrição | +|---------------|------------|-----------| +| `GET` | `get(key)` | Obtém um valor | +| `SET` | `set(key, value, options?)` | Define um valor | +| `DEL` | `delete(key)` / `del(key)` | Exclui uma chave | +| `HSET` | `hSet(key, field, value)` / `HSET(...)` | Define campo de hash | +| `HGETALL` | `hGetAll(key)` / `HGETALL(key)` | Obtém todos os campos do hash | +| `HVALS` | `hVals(key)` / `HVALS(key)` | Obtém todos os valores do hash | + +--- + +## Próximos passos + +- Saiba mais sobre o [KV Store](/pt-br/documentacao/produtos/store/kv-store/) +- Explore [gerenciar KV Store com Functions](/pt-br/documentacao/produtos/guias/kv-store/gerenciar-com-funcoes/) diff --git a/src/content/docs/pt-br/pages/guias/kv-store/gerenciar-com-funcoes.mdx b/src/content/docs/pt-br/pages/guias/kv-store/gerenciar-com-funcoes.mdx new file mode 100644 index 0000000000..e080e0cc19 --- /dev/null +++ b/src/content/docs/pt-br/pages/guias/kv-store/gerenciar-com-funcoes.mdx @@ -0,0 +1,321 @@ +--- +title: Como gerenciar KV Store com Functions +description: >- + Aprenda como armazenar, recuperar e excluir dados usando KV Store dentro de + suas functions. +meta_tags: 'kv store, armazenamento, chave-valor, functions, dados' +namespace: documentation_products_kv_store_manage_edge_functions +permalink: /documentacao/produtos/guias/kv-store/gerenciar-com-funcoes/ +--- + +import LinkButton from 'azion-webkit/linkbutton' + +Este guia mostra como interagir com o KV Store a partir de suas functions +para armazenar, recuperar e excluir dados chave-valor. + + + +--- + +## Requisitos + +Antes de começar, certifique-se de ter: + +- [Functions habilitadas](/pt-br/documentacao/produtos/build/edge-application/edge-functions/) em sua application. +- Um namespace do KV Store criado via Console ou API. + +--- + +## Criando uma função para interagir com o KV Store + +Siga estes passos para criar uma function que se comunica com o KV Store: + +1. Acesse o [Azion Console](https://console.azion.com). +2. No canto superior esquerdo, selecione **Functions**. +3. Clique em **+ Function**. +4. Digite um nome para sua função, por exemplo: `kv-store-handler`. +5. Exclua o código placeholder no editor. +6. Cole um dos exemplos de código abaixo de acordo com seu caso de uso. + +--- + +## Armazenando dados + +Este exemplo demonstra como armazenar diferentes tipos de dados no KV Store: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Armazenar uma string simples + await kv.put("saudacao", "Olá, Mundo!"); + + // Armazenar um objeto JSON + await kv.put("preferencias-usuario", { + tema: "escuro", + idioma: "pt-br", + notificacoes: true + }); + + // Armazenar com metadados e expiração + await kv.put("token-sessao", "abc123xyz", { + metadata: { userId: "user-42" }, + expirationTtl: 3600 // expira em 1 hora + }); + + return new Response("Dados armazenados com sucesso", { + headers: { "Content-Type": "text/plain" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Recuperando dados + +Este exemplo mostra como recuperar dados em diferentes formatos: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Obter como texto (padrão) + const saudacao = await kv.get("saudacao", "text"); + + // Obter como objeto JSON + const preferencias = await kv.get("preferencias-usuario", "json"); + + // Tratar chaves inexistentes + const inexistente = await kv.get("chave-inexistente", "text"); + if (inexistente === null) { + console.log("Chave não encontrada"); + } + + const response = { + saudacao, + preferencias, + chaveExiste: inexistente !== null + }; + + return new Response(JSON.stringify(response), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Recuperando múltiplas chaves + +Busque múltiplos valores em uma única operação: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Armazenar alguns dados de teste + await kv.put("usuario-1", "Alice"); + await kv.put("usuario-2", "Bob"); + await kv.put("usuario-3", "Charlie"); + + // Recuperar múltiplas chaves de uma vez + const chaves = ["usuario-1", "usuario-2", "usuario-3", "usuario-4"]; + const usuarios = await kv.get(chaves, "text"); + + // Resultado: { "usuario-1": "Alice", "usuario-2": "Bob", "usuario-3": "Charlie", "usuario-4": null } + + return new Response(JSON.stringify(usuarios), { + 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: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Armazenar dados com metadados + await kv.put("sessao", "dados-sessao-ativa", { + metadata: { + criadoEm: new Date().toISOString(), + userId: "user-42" + } + }); + + // Recuperar com metadados + const resultado = await kv.getWithMetadata("sessao", "text"); + + console.log(`Valor: ${resultado.value}`); + console.log(`Metadados: ${JSON.stringify(resultado.metadata)}`); + + return new Response(JSON.stringify(resultado), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Excluindo dados + +Remova uma chave do armazenamento: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + const chave = "token-sessao"; + + // Excluir a chave + await kv.delete(chave); + + return new Response(`Chave '${chave}' excluída`, { + headers: { "Content-Type": "text/plain" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Trabalhando com dados binários + +Armazene e recupere dados binários usando ArrayBuffer: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + + // Armazenar dados binários + const encoder = new TextEncoder(); + const dadosBinarios = encoder.encode("Conteúdo binário aqui").buffer; + await kv.put("chave-binaria", dadosBinarios); + + // Recuperar como ArrayBuffer + const recuperado = await kv.get("chave-binaria", "arrayBuffer"); + + // Decodificar de volta para string + const decoder = new TextDecoder("utf-8"); + const stringDecodificada = decoder.decode(recuperado); + + return new Response(stringDecodificada, { + headers: { "Content-Type": "text/plain" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Trabalhando com streams + +Para dados grandes, use ReadableStream para uso eficiente de memória: + +```javascript +async function handleRequest(request) { + const kv = new Azion.KV(); + const chave = "conteudo-grande"; + + // Recuperar como stream + const stream = await kv.get(chave, "stream"); + + if (stream === null) { + return new Response("Chave não encontrada", { status: 404 }); + } + + // Retornar o stream diretamente na resposta + return new Response(stream, { + headers: { "Content-Type": "application/octet-stream" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Usando namespaces específicos + +Trabalhe com diferentes namespaces para isolamento de dados: + +```javascript +async function handleRequest(request) { + // Usar um namespace específico + const producaoKV = new Azion.KV("dados-producao"); + const stagingKV = new Azion.KV("dados-staging"); + + // Ou usar o método open + const cacheKV = await Azion.KV.open("namespace-cache"); + + // Cada namespace é isolado + await producaoKV.put("config", "valor-producao"); + await stagingKV.put("config", "valor-staging"); + + const configProducao = await producaoKV.get("config", "text"); + const configStaging = await stagingKV.get("config", "text"); + + return new Response(JSON.stringify({ + producao: configProducao, + staging: configStaging + }), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); +} + +addEventListener("fetch", (event) => { + event.respondWith(handleRequest(event.request)); +}); +``` + +--- + +## Instanciando a função + +Após criar sua função, você precisa instanciá-la em sua application: + + + +--- + +## Próximos passos + +- Saiba mais sobre o [KV Store](/pt-br/documentacao/produtos/store/kv-store/) +- Explore as [Functions](/pt-br/documentacao/produtos/build/edge-application/edge-functions/) diff --git a/src/content/docs/pt-br/pages/menu-principal/referencia/store/kv-store/kv-store.mdx b/src/content/docs/pt-br/pages/menu-principal/referencia/store/kv-store/kv-store.mdx new file mode 100644 index 0000000000..c06d67a36a --- /dev/null +++ b/src/content/docs/pt-br/pages/menu-principal/referencia/store/kv-store/kv-store.mdx @@ -0,0 +1,508 @@ +--- +title: KV Store +description: >- + KV Store é um serviço de armazenamento de chave-valor distribuído que opera + na rede da Azion, fornecendo persistência e recuperação de dados com + baixa latência. +meta_tags: 'kv, armazenamento, chave, distribuído, consultas, chave-valor' +namespace: docs_kv_store +permalink: /documentacao/produtos/store/kv-store/ +--- + +import Tag from 'primevue/tag' +import LinkButton from 'azion-webkit/linkbutton' + + + Preview + + +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 nodes da Azion 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 + +| Escopo | Recurso | +| ------ | ------- | +| Gerenciar KV Store com Functions | [Como gerenciar KV Store com Functions](/pt-br/documentacao/produtos/guias/kv-store/gerenciar-com-funcoes/) | +| Referência da API KV Store | [Azion API - KV Store](https://api.azion.com/) | + +--- + +## 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 + +```bash +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):** + +```json +{ + "name": "meu-namespace", + "created_at": "2025-01-24T14:15:22Z", + "last_modified": "2025-01-24T14:15:22Z" +} +``` + +#### Listar namespaces + +```bash +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âmetro | Tipo | Descrição | +| --------- | ---- | --------- | +| `fields` | string | Lista separada por vírgulas de nomes de campos para incluir na resposta | +| `page` | integer | Número da página dentro do conjunto de resultados paginados | +| `page_size` | integer | Número de itens por página | + +#### Recuperar um namespace + +```bash +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`: + +```javascript +// 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: + +```javascript +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: + +```javascript +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: + +```javascript +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: + +```javascript +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: + +```javascript +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: + +```javascript +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: + +```javascript +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: + +```javascript +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: + +```javascript +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étodo | Descriçã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étodo | Parâmetros | Retorna | Descrição | +| ------ | ---------- | ------- | --------- | +| `put(key, value, options?)` | `key`: string
`value`: string \| object \| ArrayBuffer \| ReadableStream
`options`: { metadata?, expiration?, expirationTtl? } | `Promise` | Armazena um valor | +| `get(key, type?, options?)` | `key`: string \| string[]
`type`: "text" \| "json" \| "arrayBuffer" \| "stream"
`options`: { cacheTtl? } | `Promise` | 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`: string | `Promise` | Exclui uma chave | + +### Opções do put + +| Opção | Tipo | Descrição | +| ----- | ---- | --------- | +| `metadata` | object | Metadados personalizados para associar à chave (máx 1024 bytes JSON-serializado) | +| `expiration` | number | Timestamp Unix (segundos) quando a chave expira | +| `expirationTtl` | number | Time-to-live em segundos a partir de agora | + +### Opções do get + +| Opção | Tipo | Descrição | +| ----- | ---- | --------- | +| `cacheTtl` | number | Tempo em segundos para armazenar o resultado em cache localmente (mínimo 60 segundos) | + +### Tipos do get + +| Tipo | Retorna | Descrição | +| ---- | ------- | --------- | +| `"text"` | string | Retorna o valor como uma string UTF-8 (padrão) | +| `"json"` | object | Faz parse do valor como JSON | +| `"arrayBuffer"` | ArrayBuffer | Retorna dados binários brutos | +| `"stream"` | ReadableStream | Retorna um stream legível para valores grandes | + +--- + +## Limites + +Estes são os **limites padrão**: + +| Limite | Valor | +| ------ | ----- | +| Taxa de gravação por chave | Até 1 gravação por segundo para a mesma chave | +| Tamanho da chave | Até 512 bytes (UTF-8) | +| Tamanho dos metadados | Até 1024 bytes (JSON-serializado) | +| Tamanho do valor | Até 25 MB por item | +| Comprimento do nome do namespace | 3-63 caracteres | +| Mínimo `expirationTtl` | 60 segundos | +| Mínimo `cacheTtl` | 60 segundos | + +Estes são os **limites padrão** para cada Plano de Serviço: + +| Escopo | Developer | Business | Enterprise | Mission Critical | +| ------ | --------- | -------- | ---------- | ---------------- | +| Namespaces | 1000 | 1000 | 1000 | 1000 | +| Tamanho máximo do arquivo | 200 MB | 500 MB | 2 GB | 2 GB | +| Armazenamento máximo por conta | 5 GB | 50 GB | 300 GB | 300 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](https://api.azion.com/) ou Console, não de dentro de functions. + +---