Files
supabase/apps/docs/content/guides/functions/examples/send-emails.mdx
Tomás Pozo adbd3c3d22 docs: migrate Edge Functions guides to @supabase/server (COM-269) (#46656)
## What

Migrates the Edge Functions **documentation** from the legacy
`Deno.serve` + manual `createClient` pattern to the
[`@supabase/server`](https://github.com/supabase/server) `withSupabase`
wrapper. This is the part of
[COM-269](https://linear.app/supabase/issue/COM-269) that AI coding
assistants index, so it's split out to ship first; the standalone
`examples/` functions follow in a second PR.

## Canonical pattern

```ts
import { withSupabase } from 'npm:@supabase/server@1'

export default {
  fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
    const { data } = await ctx.supabase.from('countries').select('*')
    return Response.json({ data })
  }),
}
```

- `export default { fetch }` object shape (not `Deno.serve`, not a bare
default export), versioned `npm:@supabase/server@1`.
- `auth` mode picks the caller: `user` → `ctx.supabase` (RLS);
`secret`/`publishable`/`none` → set `verify_jwt = false`, `secret` uses
`ctx.supabaseAdmin`.
- `Response.json(...)` over `new Response(JSON.stringify(...))`.

## Changes

- **AI prompt** (`examples/prompts/edge-functions.md`) — rewritten to
lead with `withSupabase` as the default; `auth`-mode table;
`@supabase/server@1`. Highest AI-indexing impact.
- **connect-to-postgres** — "Using supabase-js" now uses `ctx.supabase`
(+ its CodeSample deps `postgres-on-the-edge`, `drizzle`).
- **Example pages** — semantic-search, push-notifications,
amazon-bedrock, cloudflare-turnstile, og-image, send-emails,
slack-bot-mention, auth-send-email-hook.
- **Guides** — ai-models, background-tasks, routing (+ `restful-tasks`
dep), kysely-postgres, sentry-monitoring, upstash-redis, elevenlabs ×2,
websockets, cors (reframed: CORS is automatic with `withSupabase`).

## Notable fixes

- **websockets**: the JWT-auth examples had a latent bug — handler
wasn't `async` and called `getClaims()` without the extracted token. Now
`await supabase.auth.getUser(jwt)`. (`withSupabase` can't authenticate
WebSocket clients since they can't send headers — noted in the page.)
- **restful-tasks**: fixed a broken `npm:supabase-js` import →
`npm:@supabase/supabase-js`.

## Follow-ups (not in this PR)

- The ~42 standalone `examples/` edge functions → second PR.
- A dedicated `withSupabase` intro page (today it's only documented
inside the auth-framed "Securing Edge Functions" page).
- `.claude/skills/supabase-server/SKILL.md` is stale (`allow:` vs
`auth:`).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Updated Edge Function examples to the modern withSupabase + exported
fetch handler pattern across guides and examples.
* Standardized JSON response/error handling (uses built-in JSON helpers)
and preserved streaming/SSE behaviors where applicable.
* Clarified auth modes, context clients (user vs admin), and automatic
CORS handling; removed manual preflight boilerplate.
* Updated local serve/deploy instructions to include --no-verify-jwt for
relevant examples.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-08 12:32:15 -05:00

91 lines
2.3 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 'Sending Emails'
description: 'Sending emails from Edge Functions using the Resend API.'
tocVideo: 'Qf7XvL1fjvo'
---
Sending emails from Edge Functions using the [Resend API](https://resend.com/).
### Prerequisites
To get the most out of this guide, youll need to:
- [Create an API key](https://resend.com/api-keys)
- [Verify your domain](https://resend.com/domains)
Make sure you have the latest version of the [Supabase CLI](/docs/guides/cli#installation) installed.
### 1. Create Supabase function
Create a new function locally:
```bash
supabase functions new resend
```
Store the `RESEND_API_KEY` in your `.env` file.
### 2. Edit the handler function
Paste the following code into the `index.ts` file:
```tsx
import { withSupabase } from 'npm:@supabase/server@^1'
const RESEND_API_KEY = Deno.env.get('RESEND_API_KEY')
const handler = async (_request: Request): Promise<Response> => {
const res = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${RESEND_API_KEY}`,
},
body: JSON.stringify({
from: 'onboarding@resend.dev',
to: 'delivered@resend.dev',
subject: 'hello world',
html: '<strong>it works!</strong>',
}),
})
const data = await res.json()
return Response.json(data)
}
export default { fetch: withSupabase({ auth: ['user', 'secret'] }, handler) }
```
### 3. Deploy and send email
Run function locally:
```bash
supabase start
supabase functions serve --no-verify-jwt --env-file .env
```
The function accepts a signed-in user's JWT or a secret key, so it can be triggered from your app (via `supabase.functions.invoke`) or from a database function. Test it locally with a secret key:
```bash
curl -i --request POST 'http://localhost:54321/functions/v1/resend' \
--header 'apikey: <SUPABASE_SECRET_KEY>'
```
Deploy function to Supabase:
```bash
supabase functions deploy resend --no-verify-jwt
```
<Admonition type="caution">
When you deploy to Supabase, make sure that your `RESEND_API_KEY` is set in [Edge Function Secrets Management](/dashboard/project/_/functions/secrets)
</Admonition>
### 4. Try it yourself
Find the complete example on [GitHub](https://github.com/resendlabs/resend-supabase-edge-functions-example).