> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Streaming e caching con Supabase

> **Note**
>
> **Guida pratica** · Presuppone che tu abbia completato la [guida rapida di ElevenAPI](/docs/it/eleven-api/quickstart) e disponga di un account Supabase.

## Introduzione

In questa guida imparerai a creare un'API edge per generare, trasmettere in streaming, archiviare e memorizzare nella cache il parlato usando Supabase Edge Functions, Supabase Storage ed ElevenLabs.

## Requisiti

* Un account ElevenLabs con una [chiave API](https://elevenlabs.io/app/settings/api-keys).
* Un account [Supabase](https://supabase.com) (puoi creare un account gratuito tramite [database.new](https://database.new)).
* La [CLI di Supabase](https://supabase.com/docs/guides/local-development) installata sul tuo computer.
* Il [runtime Deno](https://docs.deno.com/runtime/getting_started/installation/) installato sul tuo computer e, facoltativamente, [configurato nel tuo IDE preferito](https://docs.deno.com/runtime/getting_started/setup_your_environment).

## Configurazione

### Crea un progetto Supabase in locale

Dopo aver installato la [CLI di Supabase](https://supabase.com/docs/guides/local-development), esegui il comando seguente per creare un nuovo progetto Supabase in locale:

```bash
supabase init
```

### Configura il bucket di storage

Puoi configurare la CLI di Supabase affinché generi automaticamente un bucket di storage aggiungendo questa configurazione al file `config.toml`:

**`./supabase/config.toml`**

```toml ./supabase/config.toml
[storage.buckets.audio]
public = false
file_size_limit = "50MiB"
allowed_mime_types = ["audio/mp3"]
objects_path = "./audio"
```

> **Note**
>
> Eseguendo `supabase start` verrà creato un nuovo bucket di storage nel tuo progetto Supabase
> locale. Se vuoi inviarlo al tuo progetto Supabase fornito, puoi eseguire `supabase seed
>   buckets --linked`.

### Configura le attività in background per Supabase Edge Functions

Per usare le attività in background in Supabase Edge Functions durante lo sviluppo locale, devi aggiungere la seguente configurazione al file `config.toml`:

**`./supabase/config.toml`**

```toml ./supabase/config.toml
[edge_runtime]
policy = "per_worker"
```

> **Note**
>
> Quando esegui con la policy `per_worker`, la Function non verrà ricaricata automaticamente dopo le modifiche. Dovrai
> riavviarla manualmente eseguendo `supabase functions serve`.

### Crea una Supabase Edge Function per la generazione del parlato

Crea una nuova Edge Function eseguendo il comando seguente:

```bash
supabase functions new text-to-speech
```

Se usi VS Code o Cursor, seleziona `y` quando la CLI chiede "Generate VS Code settings for Deno? \[y/N]"!

### Configura le variabili d'ambiente

All'interno della directory `supabase/functions`, crea un nuovo file `.env` e aggiungi le seguenti variabili:

**`supabase/functions/.env`**

```env supabase/functions/.env
# Find / create an API key at https://elevenlabs.io/app/settings/api-keys
ELEVENLABS_API_KEY=your_api_key
```

### Dipendenze

Il progetto usa alcune dipendenze:

* La libreria [@supabase/supabase-js](https://supabase.com/docs/reference/javascript) per interagire con il database Supabase.
* L'[SDK JavaScript](/docs/it/eleven-api/quickstart) di ElevenLabs per interagire con l'API Text to Speech.
* Il pacchetto open source [object-hash](https://www.npmjs.com/package/object-hash) per generare un hash dai parametri della richiesta.

Poiché Supabase Edge Function usa il [runtime Deno](https://deno.land/), non devi installare le dipendenze: puoi invece [importarle](https://docs.deno.com/examples/npm/) tramite il prefisso `npm:`.

## Scrivi il codice della Supabase Edge Function

Nel file appena creato `supabase/functions/text-to-speech/index.ts`, aggiungi il seguente codice:

**`supabase/functions/text-to-speech/index.ts`**

```ts supabase/functions/text-to-speech/index.ts
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";
import { ElevenLabsClient } from "npm:elevenlabs";
import * as hash from "npm:object-hash";

const supabase = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);

const elevenlabs = new ElevenLabsClient({
  apiKey: Deno.env.get("ELEVENLABS_API_KEY"),
});

// Upload audio to Supabase Storage in a background task
async function uploadAudioToStorage(stream: ReadableStream, requestHash: string) {
  const { data, error } = await supabase.storage
    .from("audio")
    .upload(`${requestHash}.mp3`, stream, {
      contentType: "audio/mp3",
    });

  console.log("Storage upload result", { data, error });
}

Deno.serve(async (req) => {
  // To secure your function for production, you can for example validate the request origin,
  // or append a user access token and validate it with Supabase Auth.
  console.log("Request origin", req.headers.get("host"));
  const url = new URL(req.url);
  const params = new URLSearchParams(url.search);
  const text = params.get("text");
  const voiceId = params.get("voiceId") ?? "JBFqnCBsd6RMkjVDRZzb";

  const requestHash = hash.MD5({ text, voiceId });
  console.log("Request hash", requestHash);

  // Check storage for existing audio file
  const { data } = await supabase.storage.from("audio").createSignedUrl(`${requestHash}.mp3`, 60);

  if (data) {
    console.log("Audio file found in storage", data);
    const storageRes = await fetch(data.signedUrl);
    if (storageRes.ok) return storageRes;
  }

  if (!text) {
    return new Response(JSON.stringify({ error: "Text parameter is required" }), {
      status: 400,
      headers: { "Content-Type": "application/json" },
    });
  }

  try {
    console.log("ElevenLabs API call");
    const response = await elevenlabs.textToSpeech.stream(voiceId, {
      output_format: "mp3_44100_128",
      model_id: "eleven_multilingual_v2",
      text,
    });

    const stream = new ReadableStream({
      async start(controller) {
        for await (const chunk of response) {
          controller.enqueue(chunk);
        }
        controller.close();
      },
    });

    // Branch stream to Supabase Storage
    const [browserStream, storageStream] = stream.tee();

    // Upload to Supabase Storage in the background
    EdgeRuntime.waitUntil(uploadAudioToStorage(storageStream, requestHash));

    // Return the streaming response immediately
    return new Response(browserStream, {
      headers: {
        "Content-Type": "audio/mpeg",
      },
    });
  } catch (error) {
    console.log("error", { error });
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: { "Content-Type": "application/json" },
    });
  }
});
```

### Analisi approfondita del codice

Ci sono alcuni aspetti del codice che vale la pena notare. Vediamoli passo dopo passo.

#### Gestisci la richiesta in arrivo

Per gestire la richiesta in arrivo, usa l'handler `Deno.serve`. Nella demo non convalidiamo l'origine della richiesta, ma puoi ad esempio convalidarla oppure aggiungere un token di accesso utente e convalidarlo con [Supabase Auth](https://supabase.com/docs/guides/functions/auth).

Dalla richiesta in arrivo, la funzione estrae i parametri `text` e `voiceId`. Il parametro `voiceId` è facoltativo e usa per impostazione predefinita l'ID ElevenLabs della voce "Allison".

Usando la libreria `object-hash`, la funzione genera un hash dai parametri della richiesta. Questo hash viene usato per verificare la presenza di file audio esistenti in Supabase Storage.

```ts {1,5-8}
Deno.serve(async (req) => {
// To secure your function for production, you can for example validate the request origin,
// or append a user access token and validate it with Supabase Auth.
console.log("Request origin", req.headers.get("host"));
const url = new URL(req.url);
const params = new URLSearchParams(url.search);
const text = params.get("text");
const voiceId = params.get("voiceId") ?? "JBFqnCBsd6RMkjVDRZzb";

const requestHash = hash.MD5({ text, voiceId });
console.log("Request hash", requestHash);

// ...
})
```

#### Verifica la presenza di un file audio in Supabase Storage

Supabase Storage include una [CDN intelligente integrata](https://supabase.com/docs/guides/storage/cdn/smart-cdn) che ti consente di memorizzare facilmente i file nella cache e fornirli.

Qui, la funzione verifica la presenza di un file audio in Supabase Storage. Se il file esiste, la funzione restituisce il file da Supabase Storage.

```ts {4,9}
const { data } = await supabase
  .storage
  .from("audio")
  .createSignedUrl(`${requestHash}.mp3`, 60);

if (data) {
  console.log("Audio file found in storage", data);
  const storageRes = await fetch(data.signedUrl);
  if (storageRes.ok) return storageRes;
}
```

#### Genera il parlato come stream e dividilo in due rami

Usando le capacità di streaming dell'API ElevenLabs, la funzione genera uno stream. Il vantaggio è che, anche con testi più lunghi, puoi iniziare subito a trasmettere l'audio in streaming al tuo utente e caricare poi lo stream su Supabase Storage in background.

Questo offre la migliore esperienza utente possibile, facendo sembrare incredibilmente rapidi anche grandi blocchi di testo. Il punto chiave è alla riga 17, dove il metodo `stream.tee()` divide il readablestream in due rami: uno per il browser e uno per Supabase Storage.

```ts {1,17,20,22-27}
try {
  const response = await elevenlabs.textToSpeech.stream(voiceId, {
    output_format: "mp3_44100_128",
    model_id: "eleven_multilingual_v2",
    text,
  });

  const stream = new ReadableStream({
    async start(controller) {
      for await (const chunk of response) {
        controller.enqueue(chunk);
      }
      controller.close();
    },
  });

  // Branch stream to Supabase Storage
  const [browserStream, storageStream] = stream.tee();

  // Upload to Supabase Storage in the background
  EdgeRuntime.waitUntil(uploadAudioToStorage(storageStream, requestHash));

  // Return the streaming response immediately
  return new Response(browserStream, {
    headers: {
      "Content-Type": "audio/mpeg",
    },
  });
} catch (error) {
  console.log("error", { error });
  return new Response(JSON.stringify({ error: error.message }), {
    status: 500,
    headers: { "Content-Type": "application/json" },
  });
}
```

#### Carica lo stream audio su Supabase Storage in background

Il metodo `EdgeRuntime.waitUntil` alla riga 20 del passaggio precedente viene usato per caricare lo stream audio su Supabase Storage in background tramite la funzione `uploadAudioToStorage`. Questo consente alla funzione di restituire immediatamente la risposta in streaming al browser mentre l'audio viene caricato su Supabase Storage.

Una volta creato l'oggetto di storage, la prossima volta che gli utenti effettueranno una richiesta con gli stessi parametri, la funzione restituirà il file audio dalla CDN di Supabase Storage.

```ts {2,8-10}
// Upload audio to Supabase Storage in a background task
async function uploadAudioToStorage(
  stream: ReadableStream,
  requestHash: string,
) {
  const { data, error } = await supabase.storage
    .from("audio")
    .upload(`${requestHash}.mp3`, stream, {
      contentType: "audio/mp3",
    });

  console.log("Storage upload result", { data, error });
}
```

## Esegui in locale

Per eseguire la funzione in locale, esegui i seguenti comandi:

```bash
supabase start
```

Una volta avviato lo stack Supabase locale, esegui il comando seguente per avviare la funzione e visualizzare i log:

```bash
supabase functions serve
```

### Provalo

Vai a `http://127.0.0.1:54321/functions/v1/text-to-speech?text=hello%20world` per ascoltare la funzione in azione.

Poi vai a `http://127.0.0.1:54323/project/default/storage/buckets/audio` per vedere il file audio nel bucket Supabase Storage locale.

## Esegui il deploy su Supabase

Se non l'hai già fatto, crea un nuovo account Supabase su [database.new](https://database.new) e collega il progetto locale al tuo account Supabase:

```bash
supabase link
```

Una volta fatto, esegui il comando seguente per distribuire la funzione:

```bash
supabase functions deploy
```

### Imposta i secret della funzione

Ora che hai configurato tutti i secret in locale, puoi eseguire il comando seguente per impostarli nel tuo progetto Supabase:

```bash
supabase secrets set --env-file supabase/functions/.env
```

## Testa la funzione

La funzione è progettata per poter essere usata direttamente come sorgente di un elemento `<audio>`.

```html
<audio
  src="https://${SUPABASE_PROJECT_REF}.supabase.co/functions/v1/text-to-speech?text=Hello%2C%20world!&voiceId=JBFqnCBsd6RMkjVDRZzb"
  controls
/>
```

## Passaggi successivi

#### [Streaming TTS](/docs/it/eleven-api/guides/how-to/text-to-speech/streaming)

Trasmetti l'audio progressivamente senza Supabase, per un punto di partenza più semplice.

#### [Riferimento API](/docs/it/api-reference/text-to-speech/stream)

Riferimento e parametri completi dell'API di streaming TTS.