Handling WebSockets
Handle WebSocket connections in Edge Functions.
Edge Functions supports hosting WebSocket servers that can facilitate bi-directional communications with browser clients.
This allows you to:
- Build real-time applications like chat or live updates
- Create WebSocket relay servers for external APIs
- Establish both incoming and outgoing WebSocket connections
Creating WebSocket servers
Here are some basic examples of setting up WebSocket servers using Deno and Node.js APIs.
1Deno.serve((req) => {2 const upgrade = req.headers.get('upgrade') || ''34 if (upgrade.toLowerCase() != 'websocket') {5 return new Response("request isn't trying to upgrade to WebSocket.", { status: 400 })6 }78 const { socket, response } = Deno.upgradeWebSocket(req)910 socket.onopen = () => console.log('socket opened')11 socket.onmessage = (e) => {12 console.log('socket message:', e.data)13 socket.send(new Date().toString())14 }1516 socket.onerror = (e) => console.log('socket errored:', e.message)17 socket.onclose = () => console.log('socket closed')1819 return response20})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.
Local development warning
$CodeSample directive with external repos is not supported in local development because it relies on a GitHub API key. Please check the preview site to see the final UI.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.
1import { createClient } from 'npm:@supabase/supabase-js@2'23const supabase = createClient(4 Deno.env.get('SUPABASE_URL'),5 Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')6)78Deno.serve((req) => {9 const upgrade = req.headers.get('upgrade') || ''10 if (upgrade.toLowerCase() != 'WebSocket') {11 return new Response("request isn't trying to upgrade to WebSocket.", { status: 400 })12 }1314 // Please be aware query params may be logged in some logging systems.15 const url = new URL(req.url)16 const jwt = url.searchParams.get('jwt')1718 if (!jwt) {19 console.error('Auth token not provided')20 return new Response('Auth token not provided', { status: 403 })21 }2223 const { error, data } = await supabase.auth.getClaims()2425 if (error) {26 console.error(error)27 return new Response('Invalid token provided', { status: 403 })28 }2930 if (!data.user) {31 console.error('user is not authenticated')32 return new Response('User is not authenticated', { status: 403 })33 }3435 const { socket, response } = Deno.upgradeWebSocket(req)3637 socket.onopen = () => console.log('socket opened')38 socket.onmessage = (e) => {39 console.log('socket message:', e.data)40 socket.send(new Date().toString())41 }4243 socket.onerror = (e) => console.log('socket errored:', e.message)44 socket.onclose = () => console.log('socket closed')4546 return response47})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:
1[edge_runtime]2policy = "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.