when you spin up a Supabase Edge Function written in TypeScript, the first request can easily sit on a 300‑400 ms cold‑start. in a product that streams MIDI bytes or pushes real‑time telemetry, that jitter kills user experience. the pattern I keep seeing is: keep the function tiny, compile to native code, and let the edge runtime cache the Wasm module.
why the latency spikes
Supabase runs edge functions on Deno, which starts a V8 isolate per function version. the isolate loads the source bundle, resolves imports, and then executes the entry point. each of those steps touches the filesystem and the network, so you get a latency floor that is hard to push below ~200 ms without changing the runtime.
the rust‑wasm shortcut
Rust compiles to a single Wasm file with no dynamic imports. when you ship that file as the sole entry point, Deno just loads the binary into the isolate and calls the exported handler. the cost drops to the time needed to deserialize the Wasm module – usually under 50 ms on the Supabase edge.
// src/lib.rs – minimal edge handler
use supabase_edge::prelude::*;
#[event_handler]
async fn handler(event: Request) -> Result<Response, EdgeError> {
// echo payload, but keep it sync‑free
let body = event.body_bytes().await?;
Ok(Response::new().with_body(body))
}
# Cargo.toml – ensure wasm target
[package]
name = "edge_echo"
version = "0.1.0"
edition = "2021"
[dependencies]
supabase-edge = "0.2"
[lib]
crate-type = ["cdylib"]
# build for wasm32‑unknown‑unknown
cargo build --release --target wasm32-unknown-unknown
# copy the .wasm into the Supabase function folder
cp target/wasm32-unknown-unknown/release/edge_echo.wasm ./functions/echo/
deployment tricks
-
use a static
deno.jsonthat pointsmainstraight at the Wasm file – no tsconfig, no bundler. -
pin the runtime version in
supabase/config.tomlto avoid accidental upgrades that re‑introduce the V8 bundle step. - warm‑up the function in a CI step (a simple curl) so the first production request sees a hot isolate.
trade‑offs & edge cases
- binary size – a Rust‑compiled Wasm module can be 300 KB+; that’s fine for edge caches but adds bandwidth on the first fetch.
-
debugging – stack traces are mangled unless you ship source maps (
wasm-sourcemapcrate). you lose the quickdeno rundev loop, so keep a parallel TypeScript shim for local testing. - cold‑start mitigation vs cold‑restart cost – if you have many function versions, each gets its own cache. a version churn can actually increase overall latency.
- runtime limits – Supabase caps Wasm memory at 128 MiB. ensure your code stays well under; allocating large buffers will trigger a silent kill.
what i learned
switching a latency‑critical endpoint from a TypeScript bundle to a single‑file Rust Wasm module shaved ~250 ms off the 99th‑percentile latency. the gain was most noticeable under bursty traffic where Deno isolates spin up in parallel.
how are you handling cold‑start latency on edge platforms? have you tried a compiled Wasm approach, or do you rely on other warm‑up strategies? share your findings.
Top comments (0)