Handling WebSockets
How to handle WebSocket connections in Edge Functions
Edge Functions supports hosting WebSocket servers that can facilitate bi-directional communications with browser clients.
You can also establish outgoing WebSocket client connections to another server from Edge Functions (e.g., OpenAI Realtime API). You can find an example OpenAI Realtime Relay Server implementation on the supabase-community GitHub account.
Writing a WebSocket server
Here are some basic examples of setting up WebSocket servers using Deno and Node.js APIs.
_21 Deno.serve(req => {_21 const upgrade = req.headers.get("upgrade") || "";_21_21 if (upgrade.toLowerCase() != "websocket") {_21 return new Response("request isn't trying to upgrade to websocket.", { status: 400 });_21 }_21_21 const { socket, response } = Deno.upgradeWebSocket(req);_21_21 socket.onopen = () => console.log("socket opened");_21 socket.onmessage = (e) => {_21 console.log("socket message:", e.data);_21 socket.send(new Date().toString());_21 };_21_21 socket.onerror = e => console.log("socket errored:", e.message);_21 socket.onclose = () => console.log("socket closed");_21_21 return response;_21_21});
Outbound WebSockets
You can also establish an outbound WebSocket connection to another server from an Edge Function.
Combining it with incoming WebSocket servers, it's possible to use Edge Functions as a WebSocket proxy, for example as a relay server for the OpenAI Realtime API.
Authentication
WebSocket browser clients don't have the option to send custom headers. Because of this, Edge Functions won't be able to perform the usual authorization header check to verify the JWT.
You can skip the default authorization header checks by explicitly providing --no-verify-jwt
when serving and deploying functions.
To authenticate the user making WebSocket requests, you can pass the JWT in URL query params or via a custom protocol.
_44 import { createClient } from "jsr:@supabase/supabase-js@2";_44_44const supabase = createClient(_44Deno.env.get("SUPABASE_URL"),_44Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"),_44);_44Deno.serve(req => {_44const upgrade = req.headers.get("upgrade") || "";_44_44 if (upgrade.toLowerCase() != "websocket") {_44 return new Response("request isn't trying to upgrade to websocket.", { status: 400 });_44 }_44_44// Please be aware query params may be logged in some logging systems._44const url = new URL(req.url);_44const jwt = url.searchParams.get("jwt");_44if (!jwt) {_44console.error("Auth token not provided");_44return new Response("Auth token not provided", { status: 403 });_44}_44const { error, data } = await supabase.auth.getUser(jwt);_44if (error) {_44console.error(error);_44return new Response("Invalid token provided", { status: 403 });_44}_44if (!data.user) {_44console.error("user is not authenticated");_44return new Response("User is not authenticated", { status: 403 });_44}_44_44 const { socket, response } = Deno.upgradeWebSocket(req);_44_44 socket.onopen = () => console.log("socket opened");_44 socket.onmessage = (e) => {_44 console.log("socket message:", e.data);_44 socket.send(new Date().toString());_44 };_44_44 socket.onerror = e => console.log("socket errored:", e.message);_44 socket.onclose = () => console.log("socket closed");_44_44 return response;_44_44});
Limits
The maximum duration is capped based on the wall-clock, CPU, and memory limits. The Function will shutdown when it reaches one of these limits.
Testing WebSockets locally
When testing Edge Functions locally with Supabase CLI, the instances are terminated automatically after a request is completed. This will prevent keeping WebSocket connections open.
To prevent that, you can update the supabase/config.toml
with the following settings:
_10[edge_runtime]_10policy = "per_worker"
When running with per_worker
policy, Function won't auto-reload on edits. You will need to manually restart it by running supabase functions serve
.