Switch Language
Toggle Theme

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:

RuntimeCold 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 SituationRecommendation
Already using Supabase (Auth, Database, Storage)Supabase Edge Functions
Pure edge computing, no database neededCloudflare Workers
Concerned about vendor lock-inSupabase Edge Functions (Deno is open source)
Need ultra-fast cold starts (under 50ms)Cloudflare Workers
Need direct edge-to-Postgres connectionSupabase 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

  1. 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)
  1. 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_KEY are auto-injected, available directly in your code
  • Custom variables are set via Dashboard or CLI: supabase secrets set MY_VAR=value
  1. 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:

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. 1

    Step1: Install Supabase CLI

    Install Supabase CLI globally:

    ```bash
    npm install -g supabase
    ```

    After installation, run `supabase --version` to verify successful installation.
  2. 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. 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. 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. 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. 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?
The main differences are cold start time and execution location. Edge Functions cold start is ~120ms, while AWS Lambda Node.js runtime is 300-500ms. Edge Functions code runs at global edge nodes, closer to users; Lambda runs in fixed regions. Edge Functions have CPU time limits (400 seconds), while Lambda execution time is longer (up to 15 minutes max), but not suitable for edge scenarios.
Can Edge Functions use npm packages?
Not directly. Edge Functions are based on Deno runtime and don't support Node.js npm ecosystem. You need to: 1) Use Deno-compatible packages (import from deno.land or jsr.io); 2) Use native fetch API instead of HTTP libraries like axios; 3) Find Deno ecosystem alternatives (like ky instead of axios).
Do Edge Functions have execution time limits?
Yes. V8 Isolate CPU time limit is 400 seconds (soft + hard limits combined). Exceeding this limit will forcefully terminate the process. Edge Functions are designed for lightweight logic (JWT validation, request forwarding, simple calculations), not heavy computational tasks. Complex batch processing should go on central servers or dedicated worker services.
How to connect to Postgres database in Edge Function?
Recommend using Supabase's connection pooler (Pooler) to avoid connection pool exhaustion:

- 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?
Depends on your scenario:

- 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?
Two ways to configure:

- 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

Related Posts

Comments

Sign in with GitHub to leave a comment