Skip to content
Back to skills

Supabase Functions

BSecurity

Supabase Edge Functions development and deployment using Deno runtime. Use when creating serverless functions, webhooks, API endpoints, or scheduled tasks.

  • 3 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added February 8, 2026
developmentjavascripttypescriptjavabashsqlapidatabase

Works with

  • cli
  • api

Security analysis

B88/100
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Pro scans all 2 files and shows the line behind each finding

Scanned February 12, 2026

npx -y skills add adaptationio/Skrillz --skill supabase-functions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Supabase Functions?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Supabase Functions
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/adaptationio-supabase-functions/badge)](https://www.skillsdirectory.com/skills/adaptationio-supabase-functions)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: supabase-functions
description: Supabase Edge Functions development and deployment using Deno runtime. Use when creating serverless functions, webhooks, API endpoints, or scheduled tasks.
---

# Supabase Edge Functions Skill

Serverless functions with Deno runtime.

## Quick Reference

| Task | Command |
|------|---------|
| Create function | `supabase functions new <name>` |
| Serve locally | `supabase functions serve` |
| Deploy | `supabase functions deploy <name>` |
| Delete | `supabase functions delete <name>` |
| Set secrets | `supabase secrets set KEY=value` |
| List secrets | `supabase secrets list` |

## Create Function

```bash
supabase functions new hello-world
```

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

## Basic Function Structure

```typescript
// supabase/functions/hello-world/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req: Request) => {
  const { name } = await req.json()

  return new Response(
    JSON.stringify({ message: `Hello ${name}!` }),
    { headers: { "Content-Type": "application/json" } }
  )
})
```

## Local Development

### Serve All Functions

```bash
supabase functions serve
```

### Serve with Environment Variables

```bash
supabase functions serve --env-file .env
```

### Serve without JWT Verification

```bash
supabase functions serve --no-verify-jwt
```

### Test Function

```bash
curl -i --request POST \
  'http://localhost:54321/functions/v1/hello-world' \
  --header 'Authorization: Bearer <ANON_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{"name":"World"}'
```

## CORS Handling

```typescript
import "jsr:@supabase/functions-js/edge-runtime.d.ts"

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

Deno.serve(async (req: Request) => {
  // 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 }),
      { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 400 }
    )
  }
})
```

## Using Supabase Client

```typescript
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { createClient } from 'jsr:@supabase/supabase-js@2'

Deno.serve(async (req: Request) => {
  // Create Supabase client with service role
  const supabaseAdmin = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
  )

  // Or with user's JWT (respects RLS)
  const authHeader = req.headers.get('Authorization')!
  const supabaseClient = createClient(
    Deno.env.get('SUPABASE_URL') ?? '',
    Deno.env.get('SUPABASE_ANON_KEY') ?? '',
    { global: { headers: { Authorization: authHeader } } }
  )

  // Query with user context
  const { data, error } = await supabaseClient
    .from('posts')
    .select('*')

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

## Get User from JWT

```typescript
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { createClient } from 'jsr:@supabase/supabase-js@2'

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

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

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

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

## Environment Variables & Secrets

### Set Secrets

```bash
# Single secret
supabase secrets set API_KEY=abc123

# Multiple secrets
supabase secrets set API_KEY=abc123 DB_PASSWORD=secret

# From file
supabase secrets set --env-file .env
```

### Access in Function

```typescript
const apiKey = Deno.env.get('API_KEY')
const supabaseUrl = Deno.env.get('SUPABASE_URL')
const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
```

### Built-in Variables

- `SUPABASE_URL` - Project URL
- `SUPABASE_ANON_KEY` - Anon key
- `SUPABASE_SERVICE_ROLE_KEY` - Service role key
- `SUPABASE_DB_URL` - Direct database connection

## Deploy Function

### Deploy Single Function

```bash
supabase functions deploy hello-world
```

### Deploy without JWT Verification

```bash
supabase functions deploy webhook-handler --no-verify-jwt
```

### Deploy All Functions

```bash
supabase functions deploy
```

## Invoke from JavaScript

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

### With Custom Headers

```javascript
const { data, error } = await supabase.functions.invoke('api-handler', {
  body: { action: 'create' },
  headers: { 'x-custom-header': 'value' }
})
```

## Webhook Function

```typescript
// supabase/functions/stripe-webhook/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import Stripe from 'npm:stripe@14'

const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!, {
  apiVersion: '2023-10-16'
})

const cryptoProvider = Stripe.createSubtleCryptoProvider()

Deno.serve(async (req: Request) => {
  const signature = req.headers.get('Stripe-Signature')!
  const body = await req.text()

  let event: Stripe.Event

  try {
    event = await stripe.webhooks.constructEventAsync(
      body,
      signature,
      Deno.env.get('STRIPE_WEBHOOK_SECRET')!,
      undefined,
      cryptoProvider
    )
  } catch (err) {
    return new Response(
      JSON.stringify({ error: `Webhook Error: ${err.message}` }),
      { status: 400 }
    )
  }

  switch (event.type) {
    case 'checkout.session.completed':
      const session = event.data.object
      // Handle successful checkout
      break
    default:
      console.log(`Unhandled event type ${event.type}`)
  }

  return new Response(JSON.stringify({ received: true }))
})
```

## Database Webhook

Trigger function on database changes:

```sql
-- Create webhook trigger
CREATE OR REPLACE FUNCTION notify_function()
RETURNS trigger AS $$
BEGIN
  PERFORM net.http_post(
    url := 'https://<project>.supabase.co/functions/v1/handle-insert',
    headers := '{"Authorization": "Bearer <SERVICE_KEY>"}'::jsonb,
    body := row_to_json(NEW)::text
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER on_insert
  AFTER INSERT ON posts
  FOR EACH ROW
  EXECUTE FUNCTION notify_function();
```

## Scheduled Functions (Cron)

```sql
-- Using pg_cron extension
SELECT cron.schedule(
  'daily-cleanup',
  '0 0 * * *',  -- Every day at midnight
  $$
  SELECT net.http_post(
    url := 'https://<project>.supabase.co/functions/v1/cleanup',
    headers := '{"Authorization": "Bearer <SERVICE_KEY>"}'::jsonb
  )
  $$
);
```

## Configuration (config.toml)

```toml
[functions.hello-world]
verify_jwt = true

[functions.webhook-handler]
verify_jwt = false

[functions.api-handler]
verify_jwt = true
import_map = "./supabase/functions/import_map.json"
```

## Shared Code

Create `_shared` folder for reusable code:

```
supabase/functions/
├── _shared/
│   ├── cors.ts
│   └── supabase.ts
├── hello-world/
│   └── index.ts
└── api-handler/
    └── index.ts
```

```typescript
// _shared/cors.ts
export const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}

// _shared/supabase.ts
import { createClient } from 'jsr:@supabase/supabase-js@2'

export const supabaseAdmin = createClient(
  Deno.env.get('SUPABASE_URL')!,
  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
```

```typescript
// hello-world/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { corsHeaders } from '../_shared/cors.ts'
import { supabaseAdmin } from '../_shared/supabase.ts'

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

  // Use shared client
  const { data } = await supabaseAdmin.from('posts').select('*')

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

## Limits

| Limit | Value |
|-------|-------|
| CPU Time | 2 seconds |
| Request Timeout | 150 seconds |
| Wall Clock (Pro) | 400 seconds |
| Bundle Size | 20 MB |

## References

- [function-patterns.md](references/function-patterns.md) - Common patterns
- [deno-imports.md](references/deno-imports.md) - Import syntax

Files in this skill

  • SKILL.md8.8 KB
  • references/function-patterns.md10.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.