# Object Storage API

A Object Storage API do Azion Runtime dá a uma function de [Functions](/pt-br/documentacao/plataforma/functions/) acesso de leitura e escrita aos objetos de um bucket do [Object Storage](/pt-br/documentacao/plataforma/object-storage/). A function importa a classe `Storage` do módulo `azion:storage`, informa um bucket e chama `put`, `get`, `delete` e `list` nele. Nenhum token é usado: o construtor recebe o nome do bucket e nada mais.

> **nota**
>
> Com `azion dev`, o módulo guarda os objetos no disco local e se comporta de outra forma: `put` sem um objeto `options` falha com `TypeError: Cannot read properties of undefined (reading 'content-length')`, um número em `metadata` é aceito, uma key ausente lança `ENOENT` em vez de `StorageError: Object not found`, e `list` retorna as keys sem a barra inicial e sem `content_length`. Teste as chamadas de storage em uma function após o deploy.

---

## Construtor

Importe a classe `Storage` e crie uma instância por bucket:

```javascript
import Storage from "azion:storage";

const storage = new Storage("my-bucket");
```

| Parâmetro | Tipo   | Descrição                                                                                                 |
| --------- | ------ | --------------------------------------------------------------------------------------------------------- |
| `bucket`  | string | Nome do bucket. **Obrigatório**: sem ele, o construtor lança `StorageError: bucketName must be a string`. |

O construtor retorna um objeto `Storage` cujos métodos leem e gravam nesse bucket. Ele não verifica se o bucket existe: um nome desconhecido falha na primeira chamada de método com um erro `Bucket not found`.

O bucket é criado quando você faz o deploy de uma aplicação estática com a [Azion CLI](/pt-br/documentacao/devtools/cli/), ou pela [Azion API](https://api.azion.com/). A CLI também cria e gerencia buckets diretamente; para mais informações, consulte [Azion CLI storage](/pt-br/documentacao/devtools/cli/recursos/storage/). A configuração `workloads_access` do bucket define o que a plataforma pode fazer com os objetos dele; para mais informações, consulte [Buckets e objetos](/pt-br/documentacao/plataforma/object-storage/buckets-e-objetos/).

---

## Métodos

Todo método de `Storage` é assíncrono e retorna uma promise. Chame-o com `await`, ou passe a promise para `event.waitUntil`.

### put

`put` guarda um objeto no bucket sob uma key:

```javascript
await storage.put(key, value, options);
```

| Parâmetro | Tipo                                                                                               | Obrigatório             | Descrição                                                                                                                                                                                                                                                |
| --------- | -------------------------------------------------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`     | string                                                                                             | Sim                     | Identificador do objeto no bucket.                                                                                                                                                                                                                       |
| `value`   | `ArrayBuffer` ou [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) | Sim                     | Conteúdo do objeto. Os exemplos passam o `Uint8Array` que `TextEncoder` retorna. Uma string lança `TypeError: value must be an ArrayBuffer or a ReadableStream`. Com um `ReadableStream`, as APIs de Stream se aplicam e `content-length` é obrigatório. |
| `options` | object                                                                                             | Com um `ReadableStream` | Atributos do objeto, descritos na próxima tabela.                                                                                                                                                                                                        |

O objeto `options` recebe estes atributos:

| Atributo         | Tipo   | Padrão                     | Descrição                                                                                                                                                                               |
| ---------------- | ------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `content-type`   | string | `application/octet-stream` | Tipo de mídia do objeto, como o cabeçalho HTTP `content-type`. Descreve como o conteúdo é tratado. Opcional.                                                                            |
| `content-length` | string | —                          | Tamanho do objeto, em bytes. Obrigatório quando `value` é um `ReadableStream`; sem ele, `put` lança `TypeError: Cannot read properties of undefined (reading 'content-length')`.        |
| `metadata`       | object | —                          | Um objeto JavaScript guardado com o objeto. Todo valor de propriedade precisa ser uma string: um número lança `TypeError: serde_v8 error: invalid type; expected: string, got: Number`. |

`put` resolve com `undefined`. Ele lança um `StorageError` quando o bucket não existe.

### get

`get` recupera o objeto guardado sob uma key:

```javascript
const storageObject = await storage.get(key);
```

| Parâmetro | Tipo   | Obrigatório | Descrição                          |
| --------- | ------ | ----------- | ---------------------------------- |
| `key`     | string | Sim         | Identificador do objeto no bucket. |

`get` resolve com um [StorageObject](#storageobject). Ele lança `StorageError: Object not found` quando a key não guarda nenhum objeto, e um `StorageError` quando o bucket não existe.

### delete

`delete` remove o objeto guardado sob uma key:

```javascript
await storage.delete(key);
```

| Parâmetro | Tipo   | Obrigatório | Descrição                          |
| --------- | ------ | ----------- | ---------------------------------- |
| `key`     | string | Sim         | Identificador do objeto no bucket. |

`delete` resolve com `undefined`, também quando a key não guarda nenhum objeto. Ele lança um `StorageError` quando o bucket não existe.

### list

`list` retorna os objetos do bucket:

```javascript
const objectsList = await storage.list();
```

`list` não recebe parâmetros. Ele resolve com um [StorageObjectList](#storageobjectlist) e lança um `StorageError` quando o bucket não existe.

---

## StorageObject

`get` resolve com um `StorageObject`, que carrega o conteúdo do objeto e os atributos dele:

| Membro          | Tipo                                                                                        | Descrição                                                                                       |
| --------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `content`       | `ReadableStream`                                                                            | Somente leitura. O conteúdo do objeto, como um stream.                                          |
| `arrayBuffer()` | função assíncrona                                                                           | Retorna um `ArrayBuffer` com o conteúdo do objeto. Consome o stream `content`.                  |
| `metadata`      | [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) | Somente leitura. Os metadados guardados com o objeto. Leia um valor com `metadata.get("info")`. |
| `contentType`   | string                                                                                      | Somente leitura. O `content-type` do objeto.                                                    |
| `contentLength` | number                                                                                      | Somente leitura. Tamanho do conteúdo, em bytes.                                                 |

---

## StorageObjectList

`list` resolve com um `StorageObjectList`. O array `entries` dele contém um item por objeto, e cada item carrega duas propriedades:

| Propriedade      | Tipo   | Descrição                                                                                      |
| ---------------- | ------ | ---------------------------------------------------------------------------------------------- |
| `key`            | string | Key do objeto, com uma barra inicial: um objeto guardado como `test2` é listado como `/test2`. |
| `content_length` | number | Tamanho do objeto, em bytes.                                                                   |

---

## Exemplos

Cada exemplo é uma function completa que usa o bucket `my-bucket`. Substitua o nome por um dos seus buckets.

### Persista o corpo da requisição

Esta function transmite o corpo da requisição para o objeto `test`:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    try{
        const bucket = "my-bucket";
        const storage = new Storage(bucket);
        const key = "test";
        const inputStream = event.request.body;
        let contentLength = event.request.headers.get("content-length");
        await storage.put(key, inputStream, { "content-length": contentLength });
        return new Response("OK");
    }catch(error){
        return new Response(error, {status:500});
    }
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

Para uma requisição `POST` com um corpo de texto, a function guarda o corpo e responde `OK`.

### Persista um objeto JSON

Esta function codifica um objeto JSON e o guarda como o objeto `test`:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    try{
        const bucket = "my-bucket";
        const storage = new Storage(bucket);
        const key = "test";
        const data = JSON.stringify({
            name:"John",
            address:"Abbey Road"
        });
        const buffer = new TextEncoder().encode(data);
        await storage.put(key, buffer);
        return new Response("OK");
    }catch(error){
        return new Response(error, {status:500});
    }
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

A function responde `OK` depois que o objeto é guardado.

### Guarde um objeto JSON com metadados

Esta function guarda um objeto JSON como `test2`, com o content type `application/json` e dois valores de metadados:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    try{
        const bucket = "my-bucket";
        const storage = new Storage(bucket);
        const key = "test2";
        const data = JSON.stringify({
            name:"Paul",
            address:"Abbey Road"
        });
        const buffer = new TextEncoder().encode(data);
        const options = { "content-type": "application/json",
                          "metadata": {
                            info:"test info",
                            counter: "1",
                          }}
        await storage.put(key, buffer, options);
        return new Response("OK");
    }catch(error){
        return new Response(error, {status:500});
    }
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

A function responde `OK` depois que o objeto é guardado.

### Retorne o conteúdo de um objeto

Esta function recupera o objeto `test` e retorna o stream de conteúdo dele como o corpo da resposta:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    try{
        const bucket = "my-bucket";
        const storage = new Storage(bucket);
        const key = "test";
        const storageObject = await storage.get(key);
        return new Response(storageObject.content);
    }catch(error){
        return new Response(error, {status:500});
    }
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

Quando `test` guarda o objeto JSON do exemplo Persista um objeto JSON, a function responde com este corpo:

```text
{"name":"John","address":"Abbey Road"}
```

### Decodifique um objeto JSON

Esta function lê o objeto `test` com `arrayBuffer()` e o decodifica para texto:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    try{
        const bucket = "my-bucket";
        const storage = new Storage(bucket);
        const key = "test";
        const storageObject = await storage.get(key);
        const data = new TextDecoder().decode(await storageObject.arrayBuffer());
        return new Response(data);
    }catch(error){
        return new Response(error, {status:500});
    }
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

Quando `test` guarda o objeto JSON do exemplo Persista um objeto JSON, a function responde com este corpo:

```text
{"name":"John","address":"Abbey Road"}
```

### Leia os metadados de um objeto

Esta function recupera o objeto `test2` e retorna o valor de metadados `info` dele:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    try{
        const bucket = "my-bucket";
        const storage = new Storage(bucket);
        const key = "test2"
        const storageObject = await storage.get(key);
        return new Response(storageObject.metadata.get("info"));
    }catch(error){
        return new Response(error, {status:500});
    }
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

Quando `test2` guarda o objeto do exemplo Guarde um objeto JSON com metadados, a function responde `test info`.

### Exclua um objeto

Esta function remove o objeto `test` do bucket:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    const bucket = "my-bucket";
    const storage = new Storage(bucket);
    const key = "test";
    try{
        await storage.delete(key);
    }catch(error){
        return new Response(error, {status:500});
    }
    return new Response("Ok");
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

A function responde `Ok` depois que o objeto é removido.

### Liste os objetos de um bucket

Esta function lista os objetos do bucket e registra no log a key e o tamanho de cada um:

```javascript
import Storage from "azion:storage";

async function handleRequest(event) {
    try{
        const bucket = "my-bucket";
        const storage = new Storage(bucket);
        const objectsList = await storage.list();
        for (const entry of objectsList.entries) {
            console.log(`key: ${entry.key} length: ${entry.content_length}`);
        }
        return new Response("Ok");
    }catch(error){
        return new Response(error, {status:500});
    }
}

addEventListener("fetch", (event) => {
    event.respondWith(handleRequest(event));
});
```

Para um bucket que guarda quatro objetos, a function responde `Ok` e registra estas linhas no log:

```text
key: /examples/a.json length: 38
key: /examples/b.json length: 15
key: /examples/c.txt length: 13
key: /test2 length: 38
```

---

## Erros

Uma chamada que falha lança um erro cujo `name` é `StorageError`, ou um `TypeError` para um argumento que o método recusa. O runtime não tem uma classe global `StorageError`, então compare com `error.name`. Cada string abaixo é o erro como `new Response(error)` o retorna; `<bucket-name>` representa o nome que a function passou.

| Erro                                                                        | Causa                                                       | O que fazer                                                          |
| --------------------------------------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------- |
| `StorageError: bucketName must be a string`                                 | O construtor não recebeu um nome de bucket.                 | Passe o nome do bucket para `new Storage()`.                         |
| `StorageError: storage put error: Bucket not found: <bucket-name>`          | `put` informou um bucket que a conta não tem.               | Passe o nome de um bucket que existe.                                |
| `StorageError: Bucket not found: <bucket-name>`                             | `get` informou um bucket que a conta não tem.               | Passe o nome de um bucket que existe.                                |
| `StorageError: storage delete error: Bucket not found: <bucket-name>`       | `delete` informou um bucket que a conta não tem.            | Passe o nome de um bucket que existe.                                |
| `StorageError: storage list error: Bucket not found: <bucket-name>`         | `list` informou um bucket que a conta não tem.              | Passe o nome de um bucket que existe.                                |
| `StorageError: Object not found`                                            | `get` informou uma key que não guarda nenhum objeto.        | Verifique a key, ou chame `list` para ver as keys do bucket.         |
| `TypeError: value must be an ArrayBuffer or a ReadableStream`               | `put` recebeu outro tipo de valor, como uma string.         | Codifique o valor com `TextEncoder`, como os exemplos de JSON fazem. |
| `TypeError: Cannot read properties of undefined (reading 'content-length')` | `put` recebeu um `ReadableStream` sem `content-length`.     | Passe o tamanho em bytes como `content-length` em `options`.         |
| `TypeError: serde_v8 error: invalid type; expected: string, got: Number`    | Um valor de `metadata` passado para `put` não é uma string. | Converta todo valor de metadados para uma string.                    |

---

## Recursos relacionados

- [Object Storage](/pt-br/documentacao/plataforma/object-storage.md): Como os buckets guardam objetos, e como uma aplicação os entrega por meio de um connector.
- [Buckets e objetos](/pt-br/documentacao/plataforma/object-storage/buckets-e-objetos.md): Os níveis de `workloads_access` e as chamadas da Azion API nos mesmos buckets.
- [Azion CLI storage](/pt-br/documentacao/devtools/cli/recursos/storage.md): Crie um bucket e gerencie os objetos dele pelo terminal.
- [Handlers](/pt-br/documentacao/devtools/runtime/api-reference/handlers.md): Os formatos de handler que uma function exporta, e a requisição que cada um recebe.
