mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 02:15:05 +03:00
1 parent
1c3c3e9229
commit
e58f2edde2
2 files changed
+12
-89
No files matched your search
@@ -7,7 +7,7 @@ subtitle: '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](https://platform.openai.com/docs/guides/realtime/overview)).
|
||||
You can also establish outgoing WebSocket client connections to another server from Edge Functions (e.g., [OpenAI Realtime API](https://platform.openai.com/docs/guides/realtime/overview)). You can find an example OpenAI Realtime Relay Server implementation on the [supabase-community GitHub account](https://github.com/supabase-community/openai-realtime-console?tab=readme-ov-file#using-supabase-edge-functions-as-a-relay-server).
|
||||
|
||||
### Writing a WebSocket server
|
||||
|
||||
@@ -92,94 +92,17 @@ server.listen(8080);
|
||||
|
||||
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.
|
||||
Combining it with incoming WebSocket servers, it's possible to use Edge Functions as a WebSocket proxy, for example as a [relay server](https://github.com/supabase-community/openai-realtime-console?tab=readme-ov-file#using-supabase-edge-functions-as-a-relay-server) for the [OpenAI Realtime API](https://platform.openai.com/docs/guides/realtime/overview).
|
||||
|
||||
Here is an example of proxying messages to OpenAI Realtime API.
|
||||
|
||||
We use [Supabase Auth](/docs/guides/functions/auth#fetching-the-user) to authenticate the user who is sending the messages.
|
||||
|
||||
```ts
|
||||
import { createClient } from 'jsr:@supabase/supabase-js@2'
|
||||
|
||||
const supabase = createClient(
|
||||
Deno.env.get('SUPABASE_URL'),
|
||||
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
|
||||
)
|
||||
const OPENAI_API_KEY = Deno.env.get('OPENAI_API_KEY')
|
||||
|
||||
Deno.serve(async (req) => {
|
||||
const upgrade = req.headers.get('upgrade') || ''
|
||||
|
||||
if (upgrade.toLowerCase() != 'websocket') {
|
||||
return new Response("request isn't trying to upgrade to websocket.")
|
||||
}
|
||||
|
||||
// WebSocket browser clients does not support sending custom headers.
|
||||
// We have to use the URL query params to provide user's JWT.
|
||||
// Please be aware query params may be logged in some logging systems.
|
||||
const url = new URL(req.url)
|
||||
const jwt = url.searchParams.get('jwt')
|
||||
if (!jwt) {
|
||||
console.error('Auth token not provided')
|
||||
return new Response('Auth token not provided', { status: 403 })
|
||||
}
|
||||
const { error, data } = await supabase.auth.getUser(jwt)
|
||||
if (error) {
|
||||
console.error(error)
|
||||
return new Response('Invalid token provided', { status: 403 })
|
||||
}
|
||||
if (!data.user) {
|
||||
console.error('user is not authenticated')
|
||||
return new Response('User is not authenticated', { status: 403 })
|
||||
}
|
||||
|
||||
const { socket, response } = Deno.upgradeWebSocket(req)
|
||||
|
||||
socket.onopen = () => {
|
||||
// initiate an outbound WS connection with OpenAI
|
||||
const url = 'wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01'
|
||||
|
||||
// openai-insecure-api-key isn't a problem since this code runs in an Edge Function (not client browser)
|
||||
const openaiWS = new WebSocket(url, [
|
||||
'realtime',
|
||||
`openai-insecure-api-key.${OPENAI_API_KEY}`,
|
||||
'openai-beta.realtime-v1',
|
||||
])
|
||||
|
||||
openaiWS.onopen = () => {
|
||||
console.log('Connected to OpenAI server.')
|
||||
|
||||
socket.onmessage = (e) => {
|
||||
console.log('socket message:', e.data)
|
||||
// only send the message if openAI ws is open
|
||||
if (openaiWS.readyState === 1) {
|
||||
openaiWS.send(e.data)
|
||||
} else {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
type: 'error',
|
||||
msg: 'openAI connection not ready',
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openaiWS.onmessage = (e) => {
|
||||
console.log(e.data)
|
||||
socket.send(e.data)
|
||||
}
|
||||
|
||||
openaiWS.onerror = (e) => console.log('OpenAI error: ', e.message)
|
||||
openaiWS.onclose = (e) => console.log('OpenAI session closed')
|
||||
}
|
||||
|
||||
socket.onerror = (e) => console.log('socket errored:', e.message)
|
||||
socket.onclose = () => console.log('socket closed')
|
||||
|
||||
return response // 101 (Switching Protocols)
|
||||
})
|
||||
```
|
||||
<$CodeSample
|
||||
external={true}
|
||||
org="supabase-community"
|
||||
repo="openai-realtime-console"
|
||||
commit="0f93657a71670704fbf77c48cf54d6c9eb956698"
|
||||
path="/supabase/functions/relay/index.ts"
|
||||
meta="supabase/functions/relay/index.ts"
|
||||
lines={[[1, 3], [5, -1]]}
|
||||
/>
|
||||
|
||||
### Authentication
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import { z, type SafeParseError } from 'zod'
|
||||
import { fetchWithNextOptions } from '~/features/helpers.fetch'
|
||||
import { EXAMPLES_DIRECTORY } from '~/lib/docs'
|
||||
|
||||
const ALLOW_LISTED_GITHUB_ORGS = ['supabase'] as [string, ...string[]]
|
||||
const ALLOW_LISTED_GITHUB_ORGS = ['supabase', 'supabase-community'] as [string, ...string[]]
|
||||
|
||||
const linesSchema = z.array(z.tuple([z.coerce.number(), z.coerce.number()]))
|
||||
const linesValidator = z
|
||||
|
||||
Reference in new issue
Block a user