$e.bash-tian
$e.bash-tian•4w ago

Deno application with just a Websocket connection gives 504 in Prod Deno Deploy

I have a simple Deno application that opens a websocket connection and uses a supabase client to insert the data to the table. When I run the app locally, everything works fine, when I push the code to main and the app gets deployed, when I open the production URL, the site never fully loads and eventually I get a 504, I tried sending a basic Response with Deno.Serve but that did not work. I am not entirely sure what else to do, I started using Deno this weekend and it's also my first time using Websockets, if someone could guide me in the right direction I would hugely appreciate it! Thank you in advance and I apologize if this is not clear enough
44 Replies
kimb0x
kimb0x•4w ago
I was curious to know if Deno Deploy supported WebSockets. Apparently it does! Does your code looks something like this?
kimb0x
kimb0x•4w ago
Deno Blog
Web Streams at the Edge
Deno Deploy provides web standard streaming infrastructure to easily whip up real-time applications.
$e.bash-tian
$e.bash-tianOP•4w ago
@kimb0x not quite but Ill play with this code and see if I can get it to work, I don't really need a client-server connection. Thank you!
kimb0x
kimb0x•4w ago
Sure thing! I remember Supabase having real-time events directly on their tables... wouldn't that be enough? I mean, what is the use for the Websockets in your application?
$e.bash-tian
$e.bash-tianOP•4w ago
I have a Websocket connection to a financial data provider, my plan is to consume the live data and insert it in my table. I already have the channel subscription from my UI to supabase using the realtime feature
kimb0x
kimb0x•4w ago
Sounds pretty cool If you still have some issues with it running you can share some code and maybe I can help. I ran yesterday a small Socket IO test on Deno Deploy and it worked fine.
$e.bash-tian
$e.bash-tianOP•4w ago
Honestly I would appreciate so much if you could take a look, I can share the code and record a small video to show the behavior just to make sure it's clear what the issue is
import "@std/dotenv/load";

import { createClient } from "@supabase/supabase-js";
import { useWebSocket } from "@effection-contrib/websocket";
import { each, main } from "@effection/effection";
import { generate } from "@babia/uuid-v7";

const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
);

async function insertToSupabase(aggregate: Schema) {
try {
const { error } = await supabase
.from("test_table")
.insert({
id: generate(),
created_at: new Date().toISOString(),
...aggregate,
});

if (error) throw error;
console.log("Data inserted successfully");
} catch (error) {
console.error("Error inserting data:", error);
}
}

await main(function* () {
const socket = yield* useWebSocket(Deno.env.get("WEBSOCKET_CLUSTER_URL")!);

socket.send(JSON.stringify({
action: "auth",
params: Deno.env.get("WEBSOCKET_API_KEY"),
}));

for (const message of yield* each(socket)) {
const wsMessage = message as WebSocketMessageEvent;
const [data] = JSON.parse(wsMessage.data);

if (data.message === "authenticated") {
socket.send(
JSON.stringify({ "action": "subscribe", "params": "A.AAPL" }),
);
}

if (data.sym) {
const wsData = data as Aggregate;
insertToSupabase({
ticker: wsData.sym,
open: wsData.o,
close: wsData.c,
high: wsData.h,
low: wsData.l,
vwap: wsData.vw,
aggregate_start_time: wsData.s,
aggregate_end_time: wsData.e,
aggregate_type: wsData.ev,
});
}

yield* each.next();
}
});
import "@std/dotenv/load";

import { createClient } from "@supabase/supabase-js";
import { useWebSocket } from "@effection-contrib/websocket";
import { each, main } from "@effection/effection";
import { generate } from "@babia/uuid-v7";

const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
);

async function insertToSupabase(aggregate: Schema) {
try {
const { error } = await supabase
.from("test_table")
.insert({
id: generate(),
created_at: new Date().toISOString(),
...aggregate,
});

if (error) throw error;
console.log("Data inserted successfully");
} catch (error) {
console.error("Error inserting data:", error);
}
}

await main(function* () {
const socket = yield* useWebSocket(Deno.env.get("WEBSOCKET_CLUSTER_URL")!);

socket.send(JSON.stringify({
action: "auth",
params: Deno.env.get("WEBSOCKET_API_KEY"),
}));

for (const message of yield* each(socket)) {
const wsMessage = message as WebSocketMessageEvent;
const [data] = JSON.parse(wsMessage.data);

if (data.message === "authenticated") {
socket.send(
JSON.stringify({ "action": "subscribe", "params": "A.AAPL" }),
);
}

if (data.sym) {
const wsData = data as Aggregate;
insertToSupabase({
ticker: wsData.sym,
open: wsData.o,
close: wsData.c,
high: wsData.h,
low: wsData.l,
vwap: wsData.vw,
aggregate_start_time: wsData.s,
aggregate_end_time: wsData.e,
aggregate_type: wsData.ev,
});
}

yield* each.next();
}
});
$e.bash-tian
$e.bash-tianOP•4w ago
No description
$e.bash-tian
$e.bash-tianOP•4w ago
Me using effection has no real reason other than I found it, tried it, it allowed me to setup a connection. So there's possibly an alternative Ive tried setting the connection inside a Deno.Serve() but then it's like the http server takes over and the websocket connection is never established
kimb0x
kimb0x•4w ago
Right I'm not sure if effection is doing you any good... I understand websockets doesn't need much of a connector and Deno can handle them without issue Try this instead, see if it helps: Deno.serve((req) => { if (req.headers.get("upgrade") != "websocket") { return new Response(null, { status: 501 }); } const { socket, response } = Deno.upgradeWebSocket(req); socket.addEventListener("open", () => { console.log("a client connected!"); }); socket.addEventListener("message", (event) => { if (event.data === "ping") { socket.send("pong"); } }); return response; }); Use it as your main function there
$e.bash-tian
$e.bash-tianOP•4w ago
How do I connect to the websocket? Do I have to open the connection from my client? I have my UI hosted on vercel and they don't support serverless websocket
kimb0x
kimb0x•4w ago
Websocket is just a web protocol I think, you just call a connection from your client as ws:// instead of http:// The code above will validate is a websocket connection here: req.headers.get("upgrade") != "websocket" If it is, then Deno can turn into a websocket here: const { socket, response } = Deno.upgradeWebSocket(req); From there on you're good to go with the regular "socket" object to receive messages and send messages Your client can create that connection with the javascript included constructor: new WebSocket(url) url can be: ws://www.domain.name
$e.bash-tian
$e.bash-tianOP•4w ago
Im not sure that would solve my issue, I need the websocket connection to run as a "background process" something that is deployed, stablishes a websocket to the financial data api and that's it, I don't want/need a client dependency to start the ws connection So if I have to specify from my client to start the ws, it defeats the purpose That's why I have the supabase client in deno, so I can insert directly after getting the data, and by having the UI setup with supabase realtime feature, it just detects the insert in the db and updates the UI I could not open the ws directly in my nextjs app because of Vercel limitations for ws I believe I just need a server-server ws connection Does it make sense? By the way, I wanted to thank you for even looking into this and spending time on it
kimb0x
kimb0x•4w ago
Sure thing, no problem... I was curious about websockets so I just happened to be testing things out hehe To be honest, I dont think you need a websocket at all... your client can hit the supabase with a regular POST API call probably But yeah, I dont know what the financial data source requirements are
$e.bash-tian
$e.bash-tianOP•4w ago
It requires a websocket connection to get the data
kimb0x
kimb0x•4w ago
The connection is opened automatically and stays open until the client closes it... so there's maybe not much to do on the opening and closing as long is done only once and never closed
$e.bash-tian
$e.bash-tianOP•4w ago
Yeah, I thought about that but I wont be able to have a longlived connection as long as the app is deployed on vercel...so I thought I could simply spin a server to start the connection and insert to the db
kimb0x
kimb0x•4w ago
True, I remember Vercel having only edge short-lived connections
kimb0x
kimb0x•4w ago
Oh wait, are you running your code on Supabase's Deno server? Because Supabase also doesn't support websockets... You need to use Deno Deploy https://deno.com/deploy for the websockets to run
Deno
Deno Deploy | Deno
Run JavaScript, TypeScript, and WebAssembly at the edge, worldwide.
$e.bash-tian
$e.bash-tianOP•4w ago
I am running a Deno Deploy server and Im connecting to the supabase client from the deno application to insert to the database Not on a supabase server
kimb0x
kimb0x•4w ago
Oh ok...
$e.bash-tian
$e.bash-tianOP•4w ago
Ill try to draw a diagram so its a bit more clear the high level architecture
kimb0x
kimb0x•4w ago
That would help for sure Do you get any other error on the logs from Deno Deploy? Besides the error page? Maybe also any warning at deploy build?
$e.bash-tian
$e.bash-tianOP•4w ago
This is more or less what I am aiming for, it works fine locally, but in production, the deno deploy server eventually fails, so far Ive seen 502 and 504. No warning or error logs in Deno
No description
kimb0x
kimb0x•4w ago
Right... I can better understand it So you mean it runs but it stops at some point?
$e.bash-tian
$e.bash-tianOP•4w ago
yep, but if it runs locally, it runs just fine. I did some reading and some people seem to send pings to keep to connection alive but in my case, its not that the ws goes silent and then it stops, as you can see in the video I sent earlier, the browser never fully loads, so I guess thats why the timeout
kimb0x
kimb0x•4w ago
Oh ok Does your code has a Deno.serve somewhere? Maybe locally it runs as an application but in Deno Deploy it tries to keep it alive as a server with no handler available? Im totally lost how effection works tbh
$e.bash-tian
$e.bash-tianOP•4w ago
I think I just fixed it, let me deploy and check in prod
kimb0x
kimb0x•4w ago
Awesome! Cool... crossing fingers 🤞 hehe
$e.bash-tian
$e.bash-tianOP•4w ago
so far so good in prod ill let it run for some time and see, usually i got the error in the first 5-10 mins
kimb0x
kimb0x•4w ago
Cool! Sure thing, hope it continues like this But lets not jinx it hehe
$e.bash-tian
$e.bash-tianOP•4w ago
A good sign so far is that the server resolves the initial request to "/", it no longer stays loading
kimb0x
kimb0x•4w ago
Indeed... whatever process was left hanging is now correctly resolved Looking good then
$e.bash-tian
$e.bash-tianOP•4w ago
ok...good news and bad news...deno ws is now working fine! bad news...for some reason, data stops being inserted to the database, idk if that is deno or vercel having a time limit Probably need to ping every now and then to make sure the server or ws dont think they are sending info for no reason it stayed open for around 6 minutes
kimb0x
kimb0x•4w ago
Good news for sure!... aaaand a new challenge hehe
$e.bash-tian
$e.bash-tianOP•4w ago
Isn't that the day to day of engineering? 😂 ill add sending a ping with an interval and see if that does it
kimb0x
kimb0x•4w ago
Supabase and Vercel both are shor-lived so yeah, somewhere there's something cutting the connection off but there's no WS in that section so I can't guess what could be doing it
kimb0x
kimb0x•4w ago
Hopefully the ping will do it... someone already mentioned as you said Well, I need to head off for a while... nice chatting with ya sebash-tian! Hope to see you around
$e.bash-tian
$e.bash-tianOP•4w ago
I appreciate all the help and the time you spent on this! Let me know if you want me to keep you posted, hope you have a great time!
NDH
NDH•4w ago
You could setup an SSE connection from your client to the Deploy service. These tend to auto-reconnect the server (isolate) if it goes to sleep. This would work as long as you're not holding any state in the Deploy service. When the isolate drops, the client will auto-connect in a second or two. No pings required. I have some experience with this if you need. https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#closing_event_streams
By default, if the connection between the client and server closes, the connection is restarted.
By default, if the connection between the client and server closes, the connection is restarted.
That is, the client will auto-reconnect!
And, the docs also state:
Note: The comment line can be used to prevent connections from timing out; a server can send a comment periodically to keep the connection alive.
Note: The comment line can be used to prevent connections from timing out; a server can send a comment periodically to keep the connection alive.
Below, the server should attempt to keep itself alive to deliver the stream!
And, if not, the client will attempt to auto-reconnect (continuously).
Deno -> server-side:
async function* sse() {
while (true) { // every minute you send a comment line
await new Promise(r => setTimeout(r, 60000));
yield ': comments are ignored by the client \n\n';
}
}

router.get('/sse', contentType(['text/event-stream']), (req, { type }) => {
return new StreamResponse(sse(), { headers: [['content-type', type]] })
})
async function* sse() {
while (true) { // every minute you send a comment line
await new Promise(r => setTimeout(r, 60000));
yield ': comments are ignored by the client \n\n';
}
}

router.get('/sse', contentType(['text/event-stream']), (req, { type }) => {
return new StreamResponse(sse(), { headers: [['content-type', type]] })
})
Quick questions!
How do you start the Deploy service?
How would you ping that service?
$e.bash-tian
$e.bash-tianOP•4w ago
Hi @NDH! im sorry I haven't had time to check the suggestions you made. Answering your questions, I go to Deno Deploy overview and open the site. You mean ping to keep it alive? Possibly with a similar approach to what you suggested, with the SSE UPDATE - I am pretty sure the issue is somewhere in the Deno Deploy not being set properly, I could be not handling as I should the websocket connection, when I checked with the data provider, they log the error as Client Disconnected, I have a console.error for the onerror and a console.log for the onclose...despite that I don't see anything in the deno deploy logs...I will probably simply run the deno server locally and spend time working on the UI
NDH
NDH•4w ago
That is normal Deploy isolate operation. An isolate can be expected to stop at anytime usually around 5 or 6 minutes. They are not expected to last longer without an explicitly active connection; not the WebSocket that it initiated, but the connection that you made when you started it in Deploy. That initial connection is no longer active after you start it.
If you make a browser-app with an EventSource connection(SSE), the server will maintain the stream connection, and if not, this browser-app will reestablish the connection automatically. You would only need to run the app once as a starter/maintainer. Good-Luck!
$e.bash-tian
$e.bash-tianOP•4w ago
thanks for info @NDH ! I will make sure to try it out and let you know

Did you find this page helpful?