DEV Community

eyeguide-dev
eyeguide-dev

Posted on Originally published at speakvora.com

Add spoken status messages to a web app without exposing your API key

Spoken confirmations ("Your order is ready for pickup") are easy to add, and easy to get wrong by shipping an API key in front-end code. Here is a small pattern that keeps the key on your server.

I work on Speakvora, a speech API. It is in early access (free allowance first, then pay as you go), and this post is mainly about the pattern; the same shape works with any text-to-speech API that returns an audio link.

The idea

The browser never talks to the speech API. It asks your server to speak a sentence; your server calls the API with the key and sends back only the audio URL.

1. Server route (Node 18+, Express)

import express from "express";
const app = express(); app.use(express.json());

app.post("/api/speak", async (req, res) => {
  const text = String(req.body.text || "").slice(0, 500);
  const r = await fetch("https://speakvora.com/api/v1/speak", {
    method: "POST",
    headers: { "x-api-key": process.env.SPEAKVORA_KEY, "content-type": "application/json" },
    body: JSON.stringify({ text, voice: "af_heart", lang: "en" }),
  });
  const j = await r.json();
  res.status(r.status).json(r.ok ? { url: j.url } : { error: j.error });
});
app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

2. Browser

async function say(text) {
  const r = await fetch("/api/speak", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ text }) });
  const j = await r.json();
  if (!r.ok) return console.error(j.error);
  new Audio(j.url).play(); // must follow a user gesture such as a click
}
document.querySelector("#go").onclick = () => say("Welcome back.");
Enter fullscreen mode Exit fullscreen mode

3. The errors worth handling

  • 402 free_allowance_used: the free allowance is spent.
  • 429 rate_limited: wait retry_after_seconds and retry.
  • 404 not_in_library: that voice only plays pre-recorded library lines; pick a live voice.

Things I would do next

  • Show a loading state. Phrases already in the library come back almost instantly; a brand-new sentence is generated and takes longer.
  • Keep the returned URL and reuse it rather than asking for the same sentence again.
  • Put a length limit and a rate limit on your own /api/speak route, because it spends your quota.

Full version with the Python and cURL variants: https://speakvora.com/guides/add-speech-to-a-web-app?utm_source=devto&utm_medium=article&utm_campaign=tutorial-add-speech

Free key (no card): https://speakvora.com/dashboard. Questions: support@eyeguide.ai

Top comments (0)