Supabase Edge Functions in Practice: Deno Runtime and Global Edge Deployment
It was 3 AM. I stared at the red line on my monitoring dashboard—API response times had spiked to 2.3 seconds.
My user was in Japan, but my server sat in Ohio. Light alone takes 120ms to travel that round trip, not counting database queries and business logic processing. No wonder it was slow.
“Maybe try edge functions?” a colleague messaged me on Slack.
I was skeptical. Edge functions? Isn’t that just running code in a different place? How much difference could it make? Then I ran the tests and was floored. Cold start: 120ms. Request response: 47ms. The same logic, moved from Ohio to a Tokyo edge node, ran nearly 5 times faster.
Supabase Edge Functions are built on the Deno runtime and execute your code at global edge nodes. In this article, I’ll walk you through why they’re so fast, what sets Deno apart from Node.js, how to build an Edge Function from scratch, and how to choose between Supabase and Cloudflare Workers.
1. Core Concepts: What Are Edge Functions and Why Are They Fast?
Simply put, Edge Functions move your code from “central servers” to “edge nodes.”
Previously, when you deployed a Lambda function, it ran on a server in a fixed region (like US East or Europe). A user making a request from Tokyo had to send data across the ocean to Ohio, process it, and send it back. Physical distance set a floor on latency—light speed isn’t infinite.
Edge Functions take a different approach. Your code is bundled into a compact format called ESZip and automatically distributed to dozens of edge nodes worldwide. When a user in Tokyo makes a request, the code executes right there at the Tokyo edge node, eliminating cross-ocean transmission.
What Is ESZip?
This is a bundling format created by the Deno team. Unlike traditional JavaScript bundles, ESZip packages not just the code but the entire module dependency graph. The benefit? At startup, there’s no need to fetch dependencies from the network—everything is in one file. Cold start time goes from “fetch + parse + execute” to “decompress + execute.”
Isolate Execution Model
Supabase Edge Functions run inside V8 Isolates, not traditional containers or virtual machines. What’s an Isolate? Think of it as an ultra-lightweight worker container. A single process can run dozens of Isolates, each handling one request. Lighter than containers, faster to start.
According to Supabase documentation, Isolates have a CPU time limit of 400 seconds (soft and hard limits combined). That sounds like a lot, but remember—edge functions aren’t for heavy lifting. They handle lightweight logic like JWT validation and request forwarding. Heavy computation still belongs on central servers.
Here’s the simplest Edge Function:
// Minimal Edge Function example
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
Deno.serve(async (req) => {
const { name } = await req.json()
const data = { message: `Hello ${name}!` }
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" }
})
})
This code runs at an edge node. When a request comes in, Deno.serve receives it, processes it, and returns the response. The entire process happens at the node closest to the user.
2. Deno Runtime: The “Secure Evolution” of Node.js
Honestly, when I first heard about Deno, I wondered: Node.js is so mature, why create another runtime?
The answer is straightforward: Node.js was designed too early, and many problems only surfaced later.
Security Differences
Node.js defaults to “trust everything.” Your code wants to read the filesystem? Sure. Access the network? Go ahead. Execute system commands? No problem. This means a malicious dependency can do anything.
Deno flips this around. By default, your code is sandboxed: no file access, no network access, no system commands. Want permissions? You have to explicitly declare them. For example:
# Grant network permission
deno run --allow-net server.ts
# Grant file read/write permission
deno run --allow-read --allow-write file_ops.ts
# Grant all permissions (use with caution)
deno run -A everything.ts
This design is especially important in edge environments. Your functions run on dozens of nodes worldwide—if compromised, the impact is massive.
Cold Start Differences
Here’s real-world data. I ran the same code on Deno Deploy and AWS Lambda:
| Runtime | Cold Start Time |
|---|---|
| Deno Deploy | ~120ms |
| AWS Lambda (Node.js) | 300-500ms |
A 3x difference. The reasons are complex, but the core is that Deno doesn’t carry Node.js’s historical baggage—CommonJS loading mechanism, require resolution, node_modules lookup path. ESZip bundling plus native TypeScript support eliminates a lot of startup overhead.
Module System Changes
Node.js uses CommonJS, loading modules with require(). Then you need npm, package.json, and a node_modules directory. Large projects can have hundreds of megabytes in that directory.
Deno uses the ESM standard directly. No npm, no package.json, no node_modules. Imports are URLs:
// Import directly from URL
import { serve } from "https://deno.land/[email protected]/http/server.ts"
// Or use JSR (Deno's package registry)
import { cors } from "jsr:@hono/hono/cors"
On first run, Deno downloads and caches modules locally. After that, each startup reads from cache—no more network fetches.
Zero-Config TypeScript
Running TypeScript in Node.js requires ts-node or configuring webpack/vite/esbuild, plus a tsconfig.json. Deno doesn’t need any of that. It supports TypeScript natively—just run .ts files directly:
deno run hello.ts
Compilation and type checking happen at runtime. The development experience is much cleaner.
Vendor Lock-in Concerns
This was something I worried about too. If I use Supabase Edge Functions, am I locked into their platform?
Not necessarily. Deno itself is an open-source runtime, and Supabase’s Edge Runtime is also open source (source code is on GitHub). Theoretically, you can run Edge Runtime on your own servers. Of course, you’d have to bear the operational cost of a global edge network yourself. But at least technically, there’s no “can’t run elsewhere” problem.
3. Hands-On: Build Your First Edge Function in 10 Minutes
Enough theory—let’s get hands-on.
I followed the official documentation, and from installation to deployment took less than 10 minutes. Here’s the complete process:
Step 1: Install Supabase CLI
npm install -g supabase
After installation, check the version:
supabase --version
# Output similar to: 1.200.0
Step 2: Initialize Project
If you already have a Supabase project, initialize in your project directory:
supabase init
This creates a supabase directory with a config.toml configuration file inside.
Step 3: Create Edge Function
supabase functions new hello-world
After execution, an index.ts file will be generated under supabase/functions/hello-world/ with content like:
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
Deno.serve(async (req) => {
const data = {
message: "Hello from Edge Function!"
}
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" }
})
})
You can modify it with your own logic. For example, accept a name parameter and return a greeting:
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
Deno.serve(async (req) => {
// Only accept POST requests
if (req.method !== "POST") {
return new Response("Method not allowed", { status: 405 })
}
try {
const body = await req.json()
const name = body.name || "Stranger"
return new Response(JSON.stringify({
message: `Hey ${name}, welcome to the edge!`,
timestamp: new Date().toISOString()
}), {
headers: { "Content-Type": "application/json" }
})
} catch (err) {
return new Response(JSON.stringify({ error: "Invalid JSON" }), {
status: 400,
headers: { "Content-Type": "application/json" }
})
}
})
Step 4: Local Testing
Supabase CLI supports running Edge Functions locally for easy debugging:
supabase functions serve --no-verify-jwt
This starts a local service on port 54321 by default. You can test with curl:
curl -X POST http://localhost:54321/functions/v1/hello-world \
-H "Content-Type: application/json" \
-d '{"name":"Easton"}'
# Returns:
# {"message":"Hey Easton, welcome to the edge!","timestamp":"2026-05-03T14:30:00.000Z"}
The --no-verify-jwt flag skips JWT verification for easier local testing. For production, enabling verification is recommended.
Step 5: Deploy Globally
After local testing passes, deploy to the Supabase edge network:
# Login first (if not already logged in)
supabase login
# Link your project
supabase link --project-ref <your-project-id>
# Deploy function
supabase functions deploy hello-world
After deployment, Supabase distributes your function to global edge nodes. You can see deployment logs and function URLs in the Dashboard.
Step 6: Call and Test
After the function is deployed, the URL format is:
https://<project-id>.supabase.co/functions/v1/hello-world
Call with curl:
curl -X POST https://<project-id>.supabase.co/functions/v1/hello-world \
-H "Authorization: Bearer <anon-key>" \
-H "Content-Type: application/json" \
-d '{"name":"World"}'
Note the Authorization header. Supabase Edge Functions verify JWT by default—you need to include your project’s anon key or user JWT token.
So what can Edge Functions actually do? Beyond demos, there are plenty of practical use cases:
Webhook Processing
For example, when Stripe sends a payment success webhook, you can use an Edge Function to receive it and write to the database:
// supabase/functions/stripe-webhook/index.ts
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { createClient } from "jsr:@supabase/supabase-js@2"
Deno.serve(async (req) => {
// Verify Stripe signature (simplified here, actual implementation needs hmac verification)
const event = await req.json()
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
)
if (event.type === "payment_intent.succeeded") {
const payment = event.data.object
await supabase.from("payments").insert({
id: payment.id,
amount: payment.amount,
customer_id: payment.customer,
created_at: new Date().toISOString()
})
}
return new Response(JSON.stringify({ received: true }), {
headers: { "Content-Type": "application/json" }
})
})
API Proxy
Some third-party APIs require API keys you don’t want to expose to the frontend? Use an Edge Function as a proxy:
// supabase/functions/openai-proxy/index.ts
Deno.serve(async (req) => {
const body = await req.json()
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${Deno.env.get("OPENAI_API_KEY")}`,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
})
return new Response(response.body, {
headers: { "Content-Type": "application/json" }
})
})
Your frontend calls your Edge Function directly, while API keys stay hidden on edge nodes.
4. Decision Guide: Supabase vs Cloudflare Workers
When discussing edge computing, many ask: which is better, Cloudflare Workers or Supabase Edge Functions?
Honestly, they’re not direct competitors. Different scenarios call for different choices.
Performance Comparison
Looking at cold start times, Cloudflare Workers are faster. Official data shows ~30ms versus ~120ms for Supabase Edge Functions.
Where’s the difference? Cloudflare’s Workers runtime is homegrown and specifically optimized for edge scenarios. Supabase uses Deno, which is fast but adds a TypeScript compilation layer.
The practical impact? For most scenarios, users won’t perceive the difference between 120ms and 30ms. Network fluctuations in web requests are larger than that. But for high-frequency trading or real-time bidding where milliseconds matter, Cloudflare Workers is the better choice.
Integration Comparison
This is Supabase’s strength. With Supabase Edge Functions, you can directly connect to the same project’s Postgres database, call Auth services, and access Storage. Environment variables are automatically injected—no extra configuration needed.
Cloudflare Workers requires handling database connections yourself. You can use Cloudflare D1 (their SQLite edge database) or connect to external databases, but setup takes more effort.
Vendor Lock-in
This is interesting. Cloudflare Workers’ runtime is closed-source. Code you write only runs on Cloudflare—switching platforms requires code changes.
Supabase Edge Functions uses open-source Deno. Theoretically, you can move the same code to Deno Deploy or self-host Edge Runtime. Of course, Supabase’s edge network itself can’t be moved, but at least at the runtime level, you’re not locked in.
Ecosystem Comparison
Cloudflare Workers has a more mature ecosystem. Launched in 2017, it has accumulated many tools, frameworks, and community experience. Hono and Remix frameworks both support Workers runtime.
Supabase Edge Functions launched in 2022 and is still growing. But since it uses standard Deno, many Deno libraries work directly.
Selection Recommendations
Based on your situation, here’s my rough decision matrix:
| Your Situation | Recommendation |
|---|---|
| Already using Supabase (Auth, Database, Storage) | Supabase Edge Functions |
| Pure edge computing, no database needed | Cloudflare Workers |
| Concerned about vendor lock-in | Supabase Edge Functions (Deno is open source) |
| Need ultra-fast cold starts (under 50ms) | Cloudflare Workers |
| Need direct edge-to-Postgres connection | Supabase Edge Functions |
My current approach is hybrid. Cloudflare Workers handles frontend reverse proxy and caching, Supabase Edge Functions handles Auth logic and database operations. Each platform plays to its strengths.
5. Pitfalls I Encountered and Best Practices
Let me share the pitfalls I hit in practice, so you can avoid them.
Pitfall 1: Using Node.js Packages
I wanted to use axios for HTTP requests, so I wrote import axios from "axios". Deployment failed immediately: module not found.
Edge Functions run on Deno, not Node.js. npm packages don’t work directly. You need Deno-compatible packages or use Deno’s native fetch API. Actually, fetch is sufficient:
// Don't use axios
// import axios from "axios" // This will error
// Use fetch
const response = await fetch("https://api.example.com/data", {
method: "GET",
headers: { "Authorization": "Bearer xxx" }
})
const data = await response.json()
If you really need something axios-like, try Deno ecosystem alternatives like ky.
Pitfall 2: Function Running Too Long and Getting Killed
I once wrote a batch processing function to iterate through thousands of records. Local testing was fine, but deployed it stopped after 30 seconds.
The reason is simple: Edge Functions have CPU time limits. According to official documentation, Isolate limits are 400 seconds (soft and hard limits combined). Exceed them, and the process is forcefully terminated.
Solution: Don’t do heavy work in Edge Functions. Edge nodes are for lightweight logic—validation, forwarding, simple calculations. Complex batch tasks belong on central servers or dedicated worker services.
Pitfall 3: Incorrect Database Connection Method
Connecting to Postgres in edge environments, traditional long connections cause problems. Many edge nodes each maintaining a long connection quickly exhausts the connection pool.
Recommended approach: use Supabase’s connection pooler (Pooler), or create short connections per request and release immediately. Supabase provides a SUPABASE_DB_URL environment variable that points to the Pooler:
import { Pool } from "https://deno.land/x/[email protected]/mod.ts"
const pool = new Pool(Deno.env.get("SUPABASE_DB_URL")!, 10)
Deno.serve(async (req) => {
const client = await pool.connect()
try {
const result = await client.queryArray("SELECT * FROM users LIMIT 10")
return new Response(JSON.stringify(result.rows))
} finally {
client.release() // Remember to release!
}
})
Pitfall 4: Not Configuring JWT Verification
During local testing, I used --no-verify-jwt for convenience. After deployment, I forgot to enable verification—anyone could call my function. A significant security risk.
The correct approach is configuring in config.toml:
[functions.hello-world]
verify_jwt = true
Or specify during deployment:
supabase functions deploy hello-world --verify-jwt
A Few Practical Tips
- Use Hono Framework Instead of Native Deno.serve
Hono is an ultra-lightweight web framework designed for edge environments. It supports routing and middleware while being only 13KB. Compared to Express’s bloat, Hono is perfect for Edge Functions:
import { Hono } from "jsr:@hono/hono"
import { cors } from "jsr:@hono/hono/cors"
const app = new Hono()
app.use("*", cors())
app.get("/health", (c) => c.json({ status: "ok" }))
app.post("/echo", async (c) => {
const body = await c.req.json()
return c.json({ echo: body })
})
Deno.serve(app.fetch)
- Environment Variable Management
Supabase Edge Functions have two runtimes: Main Runtime (controlled by Supabase platform) and User Runtime (your code). Environment variable permissions differ:
SUPABASE_URL,SUPABASE_ANON_KEY,SUPABASE_SERVICE_ROLE_KEYare auto-injected, available directly in your code- Custom variables are set via Dashboard or CLI:
supabase secrets set MY_VAR=value
- Import Map to Reduce Repeated Parsing
If your function depends on many external modules, use Import Map to predefine them, avoiding URL parsing on every request:
// deno.json or import_map.json
{
"imports": {
"hono": "jsr:@hono/hono",
"supabase-js": "jsr:@supabase/supabase-js@2"
}
}
Then use short names in code:
import { Hono } from "hono"
import { createClient } from "supabase-js"
Conclusion
After all this, the core message is simple: Supabase Edge Functions push your code to nodes closest to users, running in a secure Deno runtime with 120ms cold starts and automatic global distribution.
If your project already uses Supabase—whether Auth, Database, or Storage—Edge Functions are a natural extension. No need to wrestle with database connections or configure Auth verification separately—everything integrates directly. Worried about vendor lock-in? Deno is open source, Edge Runtime can be self-hosted. You at least have an “exit” option.
What’s next? Open Supabase Dashboard and run through the Quickstart. Within 10 minutes, your first Edge Function will be running on global edge nodes.
By the way, check out other articles in this series:
- Supabase Getting Started: PostgreSQL + Auth + Storage One-Stop Backend—Understand Supabase overview
- Supabase Auth in Practice: Email Verification, OAuth, and Session Management—Authentication service hands-on
- Supabase Auth Deep Configuration: OAuth, SSO, and Permission Control—Advanced configuration
If you’re more interested in Cloudflare Workers, check out my Cloudflare Workers Practical Guide. Both platforms have their strengths—choose the one that fits you best.
Deploy Your First Supabase Edge Function
Create, test, and deploy an Edge Function to the global edge network from scratch
⏱️ Estimated time: 10 min
- 1
Step1: Install Supabase CLI
Install Supabase CLI globally:
```bash
npm install -g supabase
```
After installation, run `supabase --version` to verify successful installation. - 2
Step2: Initialize Project
Run the initialization command in your project directory:
```bash
supabase init
```
This creates a `supabase` directory and `config.toml` configuration file. - 3
Step3: Create Edge Function
Use CLI to create a new Edge Function:
```bash
supabase functions new hello-world
```
The generated function is located at `supabase/functions/hello-world/index.ts` and returns a JSON response by default. - 4
Step4: Local Testing
Start the local development server:
```bash
supabase functions serve --no-verify-jwt
```
Default port is 54321. Test with curl or Postman:
```bash
curl -X POST http://localhost:54321/functions/v1/hello-world \
-H "Content-Type: application/json" \
-d '{"name":"Easton"}'
``` - 5
Step5: Deploy to Global Edge Network
After logging in and linking your project, deploy:
```bash
supabase login
supabase link --project-ref <your-project-id>
supabase functions deploy hello-world
```
After deployment, you can view the function URL and logs in the Dashboard. - 6
Step6: Call and Test
Call using the function URL and auth token:
```bash
curl -X POST https://<project-id>.supabase.co/functions/v1/hello-world \
-H "Authorization: Bearer <anon-key>" \
-H "Content-Type: application/json" \
-d '{"name":"World"}'
```
For production, enable JWT verification: `supabase functions deploy hello-world --verify-jwt`
FAQ
What's the difference between Supabase Edge Functions and AWS Lambda?
Can Edge Functions use npm packages?
Do Edge Functions have execution time limits?
How to connect to Postgres database in Edge Function?
- Use environment variable `SUPABASE_DB_URL` (automatically points to Pooler)
- Use `postgres` package to create connection pool
- Release connection after each request (`client.release()`)
Avoid traditional long connection methods—many edge nodes will exhaust the connection pool.
How to choose between Supabase Edge Functions and Cloudflare Workers?
- Already using Supabase full stack → Edge Functions (deep integration, faster development)
- Pure edge computing, no backend → Cloudflare Workers (cold start ~30ms, faster)
- Worried about vendor lock-in → Edge Functions (Deno is open source, can self-host)
- Need edge-to-Postgres direct connection → Edge Functions (seamless integration)
In practice, you can use both: Cloudflare Workers for frontend proxy and caching, Edge Functions for Auth and database logic.
How to configure JWT verification to protect Edge Function?
- Config file method: Add `[functions.hello-world] verify_jwt = true` in `config.toml`
- Command line method: Add `--verify-jwt` flag during deployment
Use `--no-verify-jwt` to skip verification for local testing, but always enable it in production. Not configuring JWT verification allows anyone to call your function.
13 min read · Published on: May 3, 2026 · Modified on: May 4, 2026
Supabase in Practice
If you landed here from search, the fastest way to build context is to jump to the previous or next post in this same series.
Previous
Supabase Storage in Practice: File Uploads, CDN, and Access Control
A complete practical guide to Supabase Storage: comparison of three access control modes, TUS chunked uploads, Smart CDN optimization tips, and cost analysis against R2/S3. Includes React code examples and troubleshooting solutions.
Part 7 of 10
Next
Supabase Realtime in Practice: Comparing Three Modes and Building Collaborative Applications
Supabase Realtime offers three real-time modes: Postgres Changes, Presence, and Broadcast. This article compares each mode's characteristics and provides complete collaborative application code examples with RLS security configurations.
Part 9 of 10
Related Posts
Supabase Getting Started: PostgreSQL + Auth + Storage All-in-One Backend
Supabase Getting Started: PostgreSQL + Auth + Storage All-in-One Backend
Supabase Database Design: Tables, Relationships & Row Level Security Guide
Supabase Database Design: Tables, Relationships & Row Level Security Guide
Supabase Auth in Practice: Email Verification, OAuth & Session Management


Comments
Sign in with GitHub to leave a comment