Edge Functions Concepts
Edge Functions là gì?
Overview
┌─────────────────────────────────────────────────────────────┐
│ SUPABASE EDGE FUNCTIONS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client ──▶ Edge Function ──▶ Supabase DB / External APIs │
│ │ │
│ │ │
│ ┌──────┴──────┐ │
│ │ Deno │ │
│ │ Runtime │ │
│ └─────────────┘ │
│ │
│ Features: │
│ - Server-side TypeScript/JavaScript │
│ - Deno runtime (secure by default) │
│ - Global edge deployment │
│ - Native Supabase integration │
│ - External API calls │
│ - Webhooks handling │
│ │
└─────────────────────────────────────────────────────────────┘
When to Use Edge Functions
Use Cases
┌─────────────────────────────────────────────────────────────┐
│ USE CASES │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ GOOD for: │
│ - External API integration (Stripe, SendGrid, etc.) │
│ - Webhooks from third-party services │
│ - Complex business logic │
│ - Data transformation before storage │
│ - Custom authentication flows │
│ - Scheduled tasks (with cron) │
│ │
│ ❌ NOT ideal for: │
│ - Simple CRUD (use REST API directly) │
│ - Database-only operations (use RPC/triggers) │
│ - Long-running tasks (30s timeout) │
│ │
└─────────────────────────────────────────────────────────────┘
Project Structure
Local Setup
# Initialize Supabase project
supabase init
# Create new function
supabase functions new my-function
# Project structure
supabase/
├── functions/
│ ├── my-function/
│ │ └── index.ts # Function code
│ ├── another-function/
│ │ └── index.ts
│ └── _shared/ # Shared utilities
│ └── supabase-client.ts
├── config.toml
└── seed.sql
Basic Function Structure
Hello World
// supabase/functions/hello/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
serve(async (req: Request) => {
const { name } = await req.json();
return new Response(
JSON.stringify({ message: `Hello ${name}!` }),
{
headers: { "Content-Type": "application/json" },
}
);
});
With Supabase Client
// supabase/functions/get-user/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
serve(async (req: Request) => {
// Create Supabase client
const supabase = createClient(
Deno.env.get("SUPABASE_URL") ?? "",
Deno.env.get("SUPABASE_ANON_KEY") ?? "",
{
global: {
headers: { Authorization: req.headers.get("Authorization")! },
},
}
);
// Get user from JWT
const { data: { user } } = await supabase.auth.getUser();
return new Response(
JSON.stringify({ user }),
{ headers: { "Content-Type": "application/json" } }
);
});
Deno Runtime
Key Differences from Node.js
// Deno uses URL imports
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
// Environment variables
const apiKey = Deno.env.get("API_KEY");
// Fetch is built-in (no node-fetch needed)
const response = await fetch("https://api.example.com");
// Top-level await is supported
const data = await response.json();
Import Map (Optional)
// supabase/functions/import_map.json
{
"imports": {
"@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2",
"std/": "https://deno.land/std@0.168.0/"
}
}
CORS Handling
Basic CORS
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers":
"authorization, x-client-info, apikey, content-type",
};
serve(async (req: Request) => {
// Handle preflight
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
// Your function logic
const data = { message: "Hello" };
return new Response(JSON.stringify(data), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
});
Environment Variables
Setting Secrets
# Set secret (production)
supabase secrets set MY_API_KEY=sk_live_xxxxx
# Set multiple
supabase secrets set \
STRIPE_KEY=sk_live_xxx \
SENDGRID_KEY=SG.xxx
# List secrets
supabase secrets list
# Local development (.env.local)
# supabase/functions/.env.local
MY_API_KEY=sk_test_xxxxx
Accessing in Function
const apiKey = Deno.env.get("MY_API_KEY");
// Built-in Supabase variables (auto-available)
const supabaseUrl = Deno.env.get("SUPABASE_URL");
const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY");
const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY");
Invoking Functions
From Client
// Using supabase-js
const { data, error } = await supabase.functions.invoke("my-function", {
body: { name: "World" },
});
// With custom headers
const { data, error } = await supabase.functions.invoke("my-function", {
body: { name: "World" },
headers: { "Custom-Header": "value" },
});
Direct HTTP
# Using curl
curl -X POST \
'https://project-ref.supabase.co/functions/v1/my-function' \
-H 'Authorization: Bearer ANON_KEY' \
-H 'Content-Type: application/json' \
-d '{"name": "World"}'
Local Development
Serve Locally
# Serve all functions
supabase functions serve
# Serve specific function
supabase functions serve my-function
# With env file
supabase functions serve --env-file ./supabase/.env.local
# Debug mode
supabase functions serve --debug
Test Locally
# Invoke local function
curl -X POST \
'http://localhost:54321/functions/v1/my-function' \
-H 'Authorization: Bearer ANON_KEY' \
-H 'Content-Type: application/json' \
-d '{"name": "Test"}'
Tổng kết
Edge Functions Basics
| Aspect |
Detail |
| Runtime |
Deno (TypeScript/JavaScript) |
| Timeout |
30 seconds |
| Memory |
150MB |
| Deployment |
Global edge network |
| Auth |
JWT from Authorization header |
Key Commands
supabase functions new <name> # Create function
supabase functions serve # Local dev
supabase functions deploy # Deploy
supabase secrets set KEY=value # Set secrets
Q&A
- Có use case nào cần Edge Functions?
- Đã làm việc với Deno chưa?
- Cần integrate external APIs nào?