# Supabase Edge Function

An AstroWay key cannot go into a frontend: anything in the bundle is visible to every visitor. An Edge Function solves it in one file: the browser calls the function, the function calls the API with the key from the project's secrets and returns only what is needed. The same file works in [Lovable](/en/integrations/lovable/) and [Bolt](/en/integrations/bolt/), because both put their backend on Supabase.

## 1. The function

```bash frame="terminal"
supabase functions new big-three
```

Replace `supabase/functions/big-three/index.ts` with:

```ts title="supabase/functions/big-three/index.ts"
const SIGNS = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
  'Libra', 'Scorpio', 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces'];

const cors = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};

const signOf = (longitude: number) => SIGNS[Math.floor(longitude / 30)];

Deno.serve(async (req) => {
  if (req.method === 'OPTIONS') return new Response('ok', { headers: cors });

  const { date, time, timezone, latitude, longitude } = await req.json();
  const res = await fetch('https://api.astroway.info/v1/chart', {
    method: 'POST',
    headers: {
      'X-Api-Key': Deno.env.get('ASTROWAY_API_KEY') ?? '',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ date, time, timezone, latitude, longitude }),
  });
  const json = await res.json();
  if (!json.ok) return Response.json({ error: json.error }, { status: res.status, headers: cors });

  const planet = (name: string) => json.data.planets.find((p: { name: string }) => p.name === name);
  return Response.json({
    sun: signOf(planet('Sun').longitude),
    moon: signOf(planet('Moon').longitude),
    rising: signOf(json.data.houses.ascendant),
    offset_used: json.data.input.timezoneOffset,
  }, { headers: cors });
});
```

## 2. The key in secrets

Create a **Server only** key scoped to `chart` with a budget, as described in [A key for an automation](/en/integrations/#a-key-for-an-automation), and put it in the project's secrets, not in the frontend `.env`:

```bash frame="terminal"
supabase secrets set ASTROWAY_API_KEY=aw_live_...
supabase functions deploy big-three
```

The function reads the secret as `Deno.env.get('ASTROWAY_API_KEY')`, and it reaches neither the code nor the browser. You can set the same secret in the Supabase dashboard under **Edge Functions → Secrets**.

## 3. Calling it from the page

```js
const { data, error } = await supabase.functions.invoke('big-three', {
  body: { date: '1990-05-15', time: '14:30:00', timezone: 'Europe/Kyiv', latitude: 50.45, longitude: 30.52 },
});
// data: { sun: 'Taurus', moon: 'Capricorn', rising: 'Virgo', offset_used: 4 }
```

`supabase-js` adds the project's `anon` key itself, so a function with the default JWT check accepts the call with no extra setup. The function passes an API error on with the same status and `error` body, so `error.message` can be shown to the user as it is.

## Worth knowing

- **`timezone` as a zone name.** `Europe/Kyiv`, not an offset: the server knows Kyiv was on UTC+4 on 1990-05-15. An empty string is refused with `400 INVALID_FIELD`, and a missing field means UTC. Details in [No-code integrations](/en/integrations/#send-the-birth-moment-not-an-offset).
- **Cost.** `POST /v1/chart` costs 20 credits. The same body within five minutes comes from cache at no charge.
- **The key's budget.** Anyone who opens your site can call the function. A budget on the key stops a looping or hostile client at the number you set, with `429 KEY_BUDGET_EXHAUSTED`.

**Note:**
Checked on 2026-11-07: the function ran locally in Deno 2 (`deno run --allow-net --allow-env`) and was called with the same body against AstroWay production. The answer: `Taurus`, `Capricorn`, `Virgo`, offset `4`; an empty `timezone` gave `400`, a missing key `401 MISSING_API_KEY`, and `OPTIONS` returned the CORS headers. Deploying to a real Supabase project was not part of the check.
