Serverless discussion
cezarsmpio opened this issue · 36 comments
Hey I'm creator of https://heartrate.overlays.dev/ and I'm using your app to do so.
However, after the last update, the app is simply closing after 3-5 seconds.
Any idea?
Communication is now done with web sockets. The crash happens because you are passing an http url into the watch app.
You know if you can get this working with websockets I might just point people to you if they can't get my local server working. There's just too many local network issues that can happen that aren't my fault. I've thought about doing something similar myself, but I just didn't have the time. Do you think you could implement calories into your overlay?
Actually I'm not sure the current watch app will let you use external URLs properly. I'm working another update that should fix this.
Actually I'm not sure the current watch app will let you use external URLs properly. I'm working another update that should fix this.
It was working just fine :) I posted it on Reddit and a lot of people started using it, they might not be able to use it anymore after the update.
Changing it to websockets may not be possible because I'm using serveless functions and they don't support running websocket servers, since I'm making this overlay for free, I need to use free services, however, they might be limited. I'm currently using Vercel + Pusher.
It works pretty much like that:
Your app -> POST -> Vercel Serveless Function -> Trigger Pusher event -> Browser listens to it.
I can implement calories, no problem :) it might not be that hard, but it would be nice if you could return the support to HTTP requests.
Thanks!
It looks like you might be able to use AWS for free if the number of requests are low enough. The reason I switched to web sockets is they should offer a faster and more stable connection.
Also it looks like Pusher might support web sockets but I'm not sure
I'm not very familiar with AWS, to be honest, my stack now is much simpler and reliable by using Serveless + Pusher. I started doing my own WebSocket solution but it ended up being too much work because I needed to manage losing connection conditions, for instance, and Pusher is now doing all of that for me.
Pusher is a WebSocket server itself. I can't use only Pusher because my serverless functions are triggering events with specific names, that's why I use the random ID, because it creates a channel with those IDs, so people can get their own heartrate data and not from somebody else.
WebSockets won't work with my current approach.
Can't you support both? WS and HTTP?
People started messaging me on Twitter asking why the overlay is not working anymore :(
I have zero experience with creating apps for iOS hehe
Another thing, how fast this should be? I'm also a streamer and I was using it on my streaming, for me, it was fast enough before, I could see the heart rate changing on the overlay right after changing on my apple watch.
Rest calls are throttled by watchOS. That's the main reason I switched. People were having periods of time where data wouldn't get through. People were also saying that their heart rate wasn't updating as fast as it should so I rewrote the code that gets the health data to hopefully be faster and more reliable. Also I uhh didn't know your thing existed so sorry about breaking it. When do those serverless functions need to trigger? Maybe we could work together on a solution? I would really like to stick with web sockets for communication.
Hummm, I see. Maybe we could only use Pusher but it has a limitation on the free plan of 200K requests/day. I had like 40.000 in one day. We can share a project on Pusher and give it a try.
Here's pusher documentation for iOS: https://pusher.com/docs/channels/getting_started/ios
If we use the same project, we no longer need the IP, just a "code", so on the app people would just need to add this code.
Apple Watch -> Connect to Pusher -> Trigger an event to a specific channel based on the ID -> Overlay subscribes to this channel with specific ID -> Show heart rate
This way we can remove Serverless functions because we will use Pusher to be the middle term.
I think you will need the secrets, so I can send you over Slack or something...
The overlay listens to this event heart-rate-${id}
, which is the event the apple watch will need to send the event to, so this ID is generated for each user. I generate a random one whenever the access my website.
My serverless function is pretty much:
const Pusher = require("pusher");
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_KEY,
secret: process.env.PUSHER_SECRET,
cluster: "us2",
});
module.exports = (req, res) => {
const { id } = req.query || {};
const { heartRate } = req.body || {};
const isRequestUnknown = !req.headers["user-agent"]
.toLowerCase()
.startsWith("apple watch health data client");
if (!heartRate || !id || isRequestUnknown) {
return res.status(404).end();
}
pusher.trigger(
"heart-rate-obs-overlay",
`heart-rate-${id}`,
{
type: "HEART_RATE",
payload: heartRate,
},
() => res.status(200).end()
);
};
Another idea, can't you make the iPhone make the HTTP request? Apple Watch only transmits the data to it, maybe it would be better performance-wise, sorry no idea if this is even possible... I think you need to have an iPhone in order to have an Apple Watch, right?
I want your overlay to be an option, but I don't want it to be the only option. Mainly because servers cost money.
The iPhone can't be part of the equation because you can't guarantee the iPhone app is alive.
Could the ID be generated on the watch?
Another thing: If you do need a rest call to get things started, the watch can do that. It's only if you do a lot of rest calls that they get throttled.
Ideally you can still get it working without any changes on my part though
Could the ID be generated on the watch?
The overlay will need to know which ID is. How could we send this ID to the overlay? Maybe it's a design challenge than a technical one.
Another thing: If you do need a rest call to get things started, the watch can do that. It's only if you do a lot of rest calls that they get throttled.
This implies I will need a WebSocket server running and like you said, servers cost money :/
Ideally you can still get it working without any changes on my part though
Yeah, I'd need to change from Serverless to AWS or Heroku and implement a WebSocket server on my part. I'll think about it!
Are you sure Pusher can't work? I keep seeing documentation talking about web sockets.
Also I think you can do serverless web sockets with AWS if you can't do them with Pusher
I will check their documentation and see what can be done, I'm not sure it can't work. I will also check AWS Serverless WebSockets.
Thanks for the ideas!
Hi @Rexios80 , i'm working on the Amazon WebSockets API, however, I need to know what's the event name you are emitting from the watch, so I can listen to it. Could you tell me?
What do you mean by event name? They're just web socket messages.
When you emit something from the apple watch, you just call emit
or any other name without any message? Just the value?
Sorry, I'm still trying to understand how Amazon WebSockets working, I'm also checking your code here: https://github.com/Rexios80/Health-Data-Server-Overlay/blob/master/public/app.js#L122
That's why I've asked.
Do you support wss://
from the apple watch or just ws://
? Not sure if it makes any difference though.
wss should work although I haven't tested it. Does the serverless websocket respond to pings? That's what determines if the watch thinks it is disconnected or not.
I was able to make it connect, no problem at all, now the problem is that I can't use AWS WebSocket the way I wanted, or at least I lack some knowledge to do that.
I trust those random IDs to know which user I need to show the data to, but I couldn't find a way I can pass dynamic values through the WSS URL. I managed to pass it through query string parameters but I receive those values only once on the $connect
event, I actually need to get the random id + heart rate information at the same time whenever the watch sends any message. I was able to receive it but without the unique id.
Another solution would be to create a WebSocket server myself and deploy it to Heroku, but I'm afraid their free services won't be good enough.
So unfortunately I will need to close the project or unless you can help me out here.
At the moment, you are sending this message heartRate:XX
, for example. If possible, it would be nice if you could send a payload that looks like this:
{
"value": "heartRate:XX",
"queryParameters": "?id=RANDOM_ID"
}
And the URL would be something like: wss://domain.com/?id=RkXbGMqEfU5afEcAJd2NP
Not sure if you can send JSON payloads though. Or something like heartRate:80:RkXbGMqEfU5afEcAJd2NP
Thanks for your support!
Is there no way to keep track of what client has what id?
Aaah, this is,the ‘bad url’ issue
@mroffbeat The normal overlay works if you want to use that. Sorry I broke his thing. The old communication method was unstable.
Is there no way to keep track of what client has what id?
I couldn't find it. Not using the WebSocket API from AWS at least.
I think you'd have to use AmazonDB with it.
Are you sure your solution is worth putting more time into? It made sense when my overlay sucked, but it's gotten a lot better recently.
Also if my app keeps growing, free tiers aren't going to cut it.
I'm closing the issue, but if you have any other questions feel free to ask.
Hey @Rexios80 just to let you know, I was able to make it work with your app again, just need to finish the AWS custom domain: https://heartrate.overlays.dev/
The flow now looks like:
Apple Watch -> WS to AWS WebSocket Gateway -> Lambda -> POST to Vercel Serverless <-> Redis to store the overlay id based on the connection id
That's awesome! What do the server costs look like though?
All free so far. If it starts growing and having many people using it simultaneously, then it might be a problem but if that's the case, I can come up with some pricing
I just wanted to let you know I'm working on an update that should greatly reduce the number of messages that get sent from the watch. This should help you stay in your free tiers.