> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/supabase/supabase/llms.txt
> Use this file to discover all available pages before exploring further.

# Edge Functions Quickstart

> Create, test, and deploy your first Supabase Edge Function in minutes

## Prerequisites

Before you begin, make sure you have:

* A Supabase project ([create one](https://app.supabase.com))
* [Supabase CLI](https://supabase.com/docs/guides/cli) installed
* [Deno](https://deno.land/#installation) installed (optional, for local development)

## Install Supabase CLI

<CodeGroup>
  ```bash macOS theme={null}
  brew install supabase/tap/supabase
  ```

  ```bash Linux theme={null}
  brew install supabase/tap/supabase
  ```

  ```bash Windows theme={null}
  scoop bucket add supabase https://github.com/supabase/scoop-bucket.git
  scoop install supabase
  ```

  ```bash npm theme={null}
  npm install -g supabase
  ```
</CodeGroup>

Verify installation:

```bash theme={null}
supabase --version
```

## Create a Function

### Initialize Your Project

If you haven't already, initialize Supabase in your project:

```bash theme={null}
supabase init
```

This creates a `supabase` directory with the following structure:

```
supabase/
├── config.toml
└── functions/
```

### Create Your First Function

Create a new function called `hello-world`:

```bash theme={null}
supabase functions new hello-world
```

This creates:

```
supabase/functions/
└── hello-world/
    └── index.ts
```

### Write Function Code

Edit `supabase/functions/hello-world/index.ts`:

```typescript theme={null}
Deno.serve(async (req) => {
  const { name } = await req.json()
  
  const data = {
    message: `Hello ${name}!`,
    timestamp: new Date().toISOString()
  }

  return new Response(
    JSON.stringify(data),
    { headers: { 'Content-Type': 'application/json' } }
  )
})
```

## Test Locally

### Start Supabase Services

Start the local Supabase stack (requires Docker):

```bash theme={null}
supabase start
```

This starts:

* PostgreSQL database
* Auth server
* Realtime server
* Storage server
* Edge Functions runtime

### Serve Your Function

Start the function locally:

```bash theme={null}
supabase functions serve hello-world
```

You should see:

```
Serving hello-world at http://localhost:54321/functions/v1/hello-world
```

### Test with curl

In another terminal, invoke the function:

```bash theme={null}
curl -L -X POST 'http://localhost:54321/functions/v1/hello-world' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Functions"}'
```

Response:

```json theme={null}
{
  "message": "Hello Functions!",
  "timestamp": "2024-03-04T10:30:00.000Z"
}
```

### Test with JavaScript

Create a test client:

```typescript theme={null}
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'http://localhost:54321',
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs'
)

const { data, error } = await supabase.functions.invoke('hello-world', {
  body: { name: 'World' }
})

if (error) {
  console.error('Error:', error)
} else {
  console.log('Response:', data)
}
```

## Deploy to Production

### Login to Supabase

Authenticate with your Supabase account:

```bash theme={null}
supabase login
```

This opens a browser to generate an access token.

### Link Your Project

Link to your Supabase project:

```bash theme={null}
supabase link --project-ref your-project-ref
```

<Tip>
  Find your project ref in the Supabase Dashboard under Settings > General.
</Tip>

### Deploy the Function

Deploy your function:

```bash theme={null}
supabase functions deploy hello-world
```

Output:

```
Deployed function hello-world in 2.3s
  https://your-project.supabase.co/functions/v1/hello-world
```

### Test Production Function

Invoke the deployed function:

```bash theme={null}
curl -L -X POST 'https://your-project.supabase.co/functions/v1/hello-world' \
  -H 'Authorization: Bearer YOUR_ANON_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Production"}'
```

<Note>
  Replace `YOUR_ANON_KEY` with your project's anon key from Settings > API in the Dashboard.
</Note>

## Add Business Logic

### Connect to Database

Access your Supabase database from the function:

```typescript theme={null}
import { createClient } from 'npm:supabase-js@2'

Deno.serve(async (req) => {
  try {
    const supabaseClient = createClient(
      Deno.env.get('SUPABASE_URL') ?? '',
      Deno.env.get('SUPABASE_ANON_KEY') ?? '',
      {
        global: {
          headers: { Authorization: req.headers.get('Authorization')! }
        }
      }
    )

    // Query the database
    const { data, error } = await supabaseClient
      .from('users')
      .select('*')
      .limit(10)

    if (error) throw error

    return new Response(
      JSON.stringify({ users: data }),
      { headers: { 'Content-Type': 'application/json' } }
    )
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { 
        status: 500,
        headers: { 'Content-Type': 'application/json' }
      }
    )
  }
})
```

### Add Authentication

Enforce authentication in your function:

```typescript theme={null}
import { createClient } from 'npm:supabase-js@2'

Deno.serve(async (req) => {
  const supabaseClient = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_ANON_KEY') ?? '',
    {
      global: {
        headers: { Authorization: req.headers.get('Authorization')! }
      }
    }
  )

  // Get authenticated user
  const { data: { user }, error: authError } = await supabaseClient.auth.getUser()

  if (authError || !user) {
    return new Response(
      JSON.stringify({ error: 'Unauthorized' }),
      { 
        status: 401,
        headers: { 'Content-Type': 'application/json' }
      }
    )
  }

  // User is authenticated
  return new Response(
    JSON.stringify({ 
      message: `Hello ${user.email}!`,
      userId: user.id
    }),
    { headers: { 'Content-Type': 'application/json' } }
  )
})
```

### Handle CORS

Enable CORS for browser requests:

```typescript theme={null}
import { corsHeaders } from 'jsr:@supabase/supabase-js@2/cors'

Deno.serve(async (req) => {
  // Handle CORS preflight
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  try {
    const { name } = await req.json()
    
    return new Response(
      JSON.stringify({ message: `Hello ${name}!` }),
      { 
        headers: { 
          ...corsHeaders, 
          'Content-Type': 'application/json' 
        }
      }
    )
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { 
        status: 400,
        headers: { 
          ...corsHeaders, 
          'Content-Type': 'application/json' 
        }
      }
    )
  }
})
```

## Real-World Example: Send Email

Create a function to send emails with Resend:

### Create the Function

```bash theme={null}
supabase functions new send-email
```

### Write the Code

```typescript theme={null}
// supabase/functions/send-email/index.ts
const RESEND_API_KEY = Deno.env.get('RESEND_API_KEY')

Deno.serve(async (req) => {
  try {
    const { to, subject, html } = await req.json()

    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@yourdomain.com',
        to,
        subject,
        html
      })
    })

    const data = await res.json()

    return new Response(
      JSON.stringify(data),
      { 
        status: 200,
        headers: { 'Content-Type': 'application/json' }
      }
    )
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { 
        status: 500,
        headers: { 'Content-Type': 'application/json' }
      }
    )
  }
})
```

### Set Environment Variable

```bash theme={null}
supabase secrets set RESEND_API_KEY=your_resend_api_key
```

### Deploy

```bash theme={null}
supabase functions deploy send-email
```

### Invoke

```typescript theme={null}
const { data, error } = await supabase.functions.invoke('send-email', {
  body: {
    to: 'user@example.com',
    subject: 'Welcome!',
    html: '<h1>Welcome to our app!</h1>'
  }
})
```

## Development Workflow

### Watch Mode

Auto-reload on file changes:

```bash theme={null}
supabase functions serve hello-world --no-verify-jwt
```

The `--no-verify-jwt` flag skips JWT verification for easier local testing.

### Use Environment Variables Locally

Create `.env.local`:

```bash theme={null}
# supabase/.env.local
RESEND_API_KEY=your_api_key
OPENAI_API_KEY=your_openai_key
```

Serve with environment variables:

```bash theme={null}
supabase functions serve --env-file ./supabase/.env.local
```

### Test with Postman

Import this cURL command into Postman:

```bash theme={null}
curl -L -X POST 'http://localhost:54321/functions/v1/hello-world' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Postman"}'
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Deploy Functions" icon="upload" href="/functions/deploy">
    Learn advanced deployment strategies and CI/CD
  </Card>

  <Card title="Environment Variables" icon="key" href="/functions/environment-variables">
    Manage secrets and configuration
  </Card>

  <Card title="Debugging" icon="bug" href="/functions/debugging">
    Debug and monitor your functions
  </Card>
</CardGroup>

## Troubleshooting

### Function Not Found

If you get a 404 error:

1. Check the function name matches the directory name
2. Verify deployment: `supabase functions list`
3. Ensure you're using the correct URL

### CORS Errors

If browser requests fail:

1. Add CORS headers to your response
2. Handle OPTIONS requests
3. Include the Authorization header in CORS config

### Authentication Errors

If auth doesn't work:

1. Verify you're passing the Authorization header
2. Check the JWT token is valid
3. Use `--no-verify-jwt` for local testing

## Resources

* [Edge Functions Examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions)
* [Supabase CLI Reference](https://supabase.com/docs/reference/cli)
* [Deno Documentation](https://deno.land/manual)
