Coding a Simple WebSocket Server in Node.js that Scales!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up covalents friends today we're going to be creating a basic websocket server we are going to be using an npm package called ws and I prefer it to something like socket IO I go into those reasons why in the video so we're just going to get right into it let's go all right so we're going to start off with our Express template the link to this repo is actually in the description below but you can use pretty much any basic Express template and we're going to start off with our index.ts which is where we actually instantiate our Express app and then we actually create the server here with this listen function and so if you actually see this listen function does return a type server so we want to actually grab that server because we're going to need it so let's just say const s equals server or cons s equals app.listen which is our server and let's pull up a terminal and I've actually already npm installed the dependencies so we need to install a few more we're going to install a what we're going to save first and we're going to install a dependency called WS which is our websocket uh package and we're also going to install type since we're using typescript so at type slash ws and we'll go ahead and install those and perfect that was nice and quick and so what we're going to do next is we're going to actually instantiate create that server so we can actually import up here we can import a type called websocket server and it tells us picks that up pretty well and we'll actually also import a type called websocket and we're going to need this websocket a little bit later we're not going to need it for the server per save we're going to need it for some of the business logic so let's go ahead and first create our websocket server so we can say const let's call it WSS for websocket Server equals we're going to new it up so websocket server and we're going to pass in some options and the option we're going to use is called No server and basically every websocket server whoa every websocket server has to be tied to an HTTP server and so we could pass in this S as the server but if we do that then it's not my cup of tea like I actually like to prefer to do all of my upgrading myself and the reason for that is that it allows for an actual important step where you can do your own authentication and things like that and so I'm going to go ahead and do this because this is typically in production this is what I'll use as opposed to just allowing the libraries to take care of everything and automatically upgrade the requests and so I'm going to want to manually upgrade and I'll show you what I mean by that in a second but basically this no server also allows us to instantiate multiple websocket servers on the same HTTP client which is also really nice and so you can do it your own way um if you want to pass in server I think there's an actual yep there's an actual thing here that you can just pass in server there but we're going to stick with no server true and we're going to upgrade manually so the way that we want to actually upgrade manually and by the way just a side note the reason we're using WS instead of socket IO is for a multitude of reasons but I prefer WS over socket IO mainly because it is smaller faster a little bit more reliable in my opinion less memory leaks and I just prefer the flexibility and I just honestly socket I O is just a little bit too bloated for me and I don't think it scales very well so I feel like this is just all you need and it does everything you know more or less that you could desire from a websocket library and uh and also I think it plays a little bit better in terms of like actually being allowed to scale out scale up that kind of thing and so um just you know for a side note when we're actually doing this we need to make sure that we understand how this is working and in my particular case like this using something like a basic Express server like this is perfect because we want some basic API capabilities but more or less you really want to keep the server clean and pretty much devoted as a websocket server at least that's my recommendation you if you're if it's a passion project you're just like a little tiny project for sure go ahead and mix your API with your websocket server but in my opinion if it's anything in production that you're going to be using and scaling out to a multitude of people you're going to want to keep your websocket server separate from your API but still have some minor API capabilities for instance like we'll get into this a little bit later but there's some cases where you may want to have a few API routes that you're exposing so this works really well we still have our API here if we go into our index you can see that we're still instantiating body parser with uh you know or this Json body parser and we still have our Route 4 API and so we still can't have API routes and so this is kind of the perfect setup that I would say for a websocket server in terms of scaling out you got to remember that node apps in particular a lot of times you want to run on a flexible environment but with websocket servers if you have multiple instances people are going to be connecting to a single instance of that server and so if you know Bob is texting Sally over a messaging app and um you know or Sally and let's just say uh Sam and you know Sally and Sam connected two separate socket servers right and then Sally texts the group Sam is never going to actually get that message because you know whichever one Bob's on he's only going to talk to that one person right so if you have two instances it's never going to actually work so we actually need a third layer that you can use kind of like a pub sub layer that'll actually allow the servers to talk to one another too so again um a lot of complications from scaling out I know this is a basic uh getting started video but it's still something that you want to consider and know about when you're actually dealing with your websocket server so again I'm sorry for the little aside but those are some seriously important notes especially when you know we're talking about a node.js environment so anyway back to to the regularly scheduled programming all right websocket server created right so now what we're going to do is we're going to manually upgrade this thing so what we can say is s dot on and it's on upgrade and basically this upgrade function uh more or less fires when a request is being turned into a socket Mark so it's basically when a request is more than just a request it is actually a socket request right and so this is actually going to get request socket and head and then we can actually do something like a you know an error function here so let's just say for for instance case we want to actually create two functions up here we can say function we can say on socket error and let's just say this would be like a pre like a pre error so this could be on socket pre-error let's just say e is a type error and for this we're just going to constant log but you may want to actually do uh you know some logic in here where we're actually doing something when there is a socket error but we also want to have a post error as well and I'll show you why we have two it helps with your error handling it makes it a little bit more finite or a little bit more granular in terms of like actually knowing what's happening in your application so now that we have s dot on upgrade we can say that you know this socket dot on and we can say error we're going to do the onsocket pre-error all right because this is actually before we've officially upgraded and and emitted this connection into our websocket server right so typically this part right here is where I would perform any type of off so realistically um you're kind of limited to cookie off in this particular case so if you're connecting from a mobile app uh this is where the API capabilities would come into hand or come in handy because if you're connecting from a mobile app what you'd probably do is send an API request with you know some sort of you know Bearer off token or JWT or something to actually authorize the person send back some sort of secret and then pass the secret up with your connection or as maybe even the first message of your connection to authorize that user um and it wouldn't typically happen during the upgrade unfortunately so there's not really a great way or what you could do is you could actually pass it through on the path itself and so you can actually pass up a path and check it on the request and if that you know path contains the token that's where you would perform that like authorization right here right and so that a lot of times you could even pass up your token as part of the initial request so um you know basically you could perform off that way or you could perform off here with like a cookie parser and you could parse the cookies and determine whether or not it has you know an access token so um there's multiple two different ways you could do this but you know I recommend that you know this is typically where you perform the auth before the upgrade so you don't actually pass this on to your websocket server let's just say so again my recommendation from the web use cookies from from a mobile app I would actually pass the auth token up in the route um and if you're using like rooms or routes or that kind of thing where you're trying to segregate things a little bit more just create a path to find your path and then make sure you stick to that syntax and you can always pass up that token somehow right and so just make sure that you parse that token and make sure that that's not actually authorized user and then right here is basically you know where you'd say like I don't know like just say if uh I don't know let's just say if you know not let's use request.headers so she's request.headers here and for whatever you know we'll say no off so we're just using this as basically something that's saying you know if the auth is bad right bad off yeah let's just say bad off how about that Russia Heather's bad off right then we would actually do something like uh let's just say you know socket dot right and you could do you know some HTTP error code let's just say you know 1.1 uh 401 unauthorized right and then all right let's see whoa slash r slash n slash r slash n right all right so anyway you can do something like this where you write to the socket and then you just destroy it so destroy the socket good to go and then just return right so you don't have to worry about that you just throw the socket nobody got in you're good to go uh now if it does happen to be good off and you're ready to keep going then you can actually call something called handle upgrade and this is on the websocket server itself so you're going to pass in the three arguments that came into your upgrade here the request socket head so we're going to pass in request socket head and then our last one is a callback where we get our actual web socket itself excuse me so we now have our websocket here it's been upgraded into our websocket server and right now we can actually call a socket dot remove listener for this error and now we can say we can remove the pre-error function and we're going to emit a connection event and we're going to pass in the websocket and the request here right so we're emitting the connection event and so what we have to do we have to actually listen for this connection event now all right and so that's where after our s dot on we are going to have a function WSS dot on and this is on our connection so we get exactly what was passed in we get our websocket and our request and in here we can actually now redefine an error function and we can say on socket post error so now we know that the post error is actually when the websocket server server is handling it in the pre-error is when our HTTP server is handling it and so now we know that like if we're in this function we've gotten all the way to the websock server and we have a problem with our websocket server but if we're in the pre-error we're actually still in the HTTP Handler so that's an important distinction that's why I like to use two different functions here but again it's up to you if you just want one error to know that it failed you're also good to go there too so all right let's keep going ws.ons we want to make sure that the websocket itself is listening for the message event and that's going to be a message and it is binary Boolean so if we just look at this real quick we can see that the message itself is raw data and the is binary is just a Boolean value about whether or not that data is binary so it's good to know we can do a wss.clients right so this is all of the connected clients dot for each and it's going to be a function in here so for each of the clients we're going to Loop through and we're going to get each client itself and we can check if client.readystate equals equals equals this is where that websocket import came in if it's open we want to client dot send the data itself and we can actually set a value here whether or not it's binary so all right and this clients here actually includes the client itself that is sending the message so that is an important distinction right now if we sent up a message if you know if it was me sending up a message to the server it would then get it and it would send the message back to me which again it could be what you want to do it might not be what you want to do right if you wanted to check if it was the actual client what you could do is you could actually check if WS you know if WS does not equal client and then you only send so this would actually send it to everything except the person sending the message so again it depends on what you're looking for there might be certain cases or certain routes that you're using where you actually only want to send that but again um you know play with it and see which use case you need it for and if that's the case you might need to send it to yourself you might need to send it to everybody now I think a lot of messaging apps like for instance WhatsApp in particular they kind of have like little check boxes in there right and a lot of times they'll automatically post your message to the UI but then they'll put a check mark when I think they receive the message back so I think a lot of people do include themselves in the message reply back but then they probably have an actual like this can be all you know Json data with some sort of message ID and you know they get the message ID back and if they've already put it in the UI they just put that little check mark in there saying that it's been received and everybody's received it right there might be a second check mark for when people read it that kind of thing but you know messaging apps have all sorts of different little you know tidbits in there that are supposed to be used for you know increasing uh you know read rate and everything like that so decreasing errors increasing reliability all that right so the last thing that would be an important one to kind of put in here would be the close function right and this is when the socket is closed so you can actually perform some cleanup here uh but you know we just might log you know uh connection closed for now but just so you know this function will fire when the socket connection is closed right and a big kind of message or a big issue with websockets in general are kind of stale connections and so there's additional logic that you can put in here I'll probably do a future video on handling stale connections and whatnot because sometimes the client will drop without the server knowing sometimes the server will drop out of the client knowing right and so there's different ways really the best way to do it is with kind of like a separate kind of heartbeat ping where you're pinging and if you're not getting any type of response back or anything like that then you actually need to kind of close that connection manually but again we'll kind of handle that in a totally different video down the road uh and yeah we'll get to that we'll get to that later for sure so again this is pretty much the basic Bare Bones of a websocket server so it's extremely simple and it really comes down to just one big for Loop which I've always kind of found a little bit ironic so um you know again the websocket server seems daunting at first but it's actually extremely easy to create and extremely easy to implement right now making it good and making able to scale that's where it gets complicated so the architecture is really the hard part about it and um you know the code itself is is pretty barebones so uh again um let's kind of keep going from here right and so let's go to our public side now and in our public app we're going to start with the actual index.html here so let's go ahead and just delete this hello world we're not going to need that but we are going to kind of start uh well I guess we could have an H1 in there why not we'll just say you know test we can change this to be you know websockets and underneath that let's do a button inside this button let's do like an open connection button so let's give let's use IDs I'm not a big fan of IDs so sorry guys but let's just do IDs we'll do get element by ID we're only going to have one of these buttons this is just a test but let's do a second one that's for closing WS close you know close connection um and then let's do a little input block so div dot input block and inside this input block we're going to have an input it's going to be type text and again ID and then also a button for sending so button let's do send message and let's see we'll do WS send all right and the last thing that we need is underneath the input block let's do a little pre I never really used preez um but again we'll give it an ID let's just say it's going to be messages and this is where we're gonna actually put all the messages that we get right all right so this is pretty much all we need uh for the index at HTML it's going to be quick and easy I'm not going to do anything too complicated I'm just going to append text to this pre here and um yeah we're not going to do any type of like text message styling but you can if you want I encourage you to kind of expand upon what we're doing here and make it look really cool and add some a bunch of CSS and whatnot but for our CSS let's actually go into styles.css here uh we'll leave that H1 green why not be a little bit different let's do the input block class and let's just say let's give it a little bit of margin so we can space it out a little bit and then finally just messages uh Let's do let's give it a height let's just say 20 M's um let's give it some overflow Auto so that it'll actually just uh uh I don't see scroll if we need it to we can kind of scroll it down as well so it'll keep scrolling down to the bottom every time um and then let's say margin top let's also do a little margin there too so 1M margin uh and then finally that'll be good for the CSS we can pull up our JavaScript now and we can remove this Express rocks all right so let's go ahead and grab all of our elements here so messages equals it's an HTML element and we can do document.getelementbyid right messages all right const WS open equals HTML element it's a button element right button element oh not BR button there we go and we can more or less just kind of copy this and it's going to be WS open right and then we're going to have the same thing for close all right WS close and then we have our send button so WS send and that's going to just be there and then finally we have our input which is going to be an input HTML input element and we'll change this and then finally here we go a lot of copying and pasting right so let's create a function here function show message and we'll say message string and in here let's just say messages Dot dot text content and we'll say plus equals we'll put a new line and then the message itself right so a new line and then that and then we'll say messages dot scroll top equals messages dot scroll height all right and that should scroll it down to the bottom there let's see might not be an optional property so let's see if we just remove that all right so we'll just remove that we can also just say you know if not messages return all right that way we won't get an error there all right so we have our show message function now right so now we can actually call that now the only thing that we might need let's show the top here let's just say let WS equals and it's going to be of type websocket all right and we are going to do our WS open Dot add event listener and it's going to be on clicks when we click that button we're actually going to open it now in your particular app you might have it when it navigates to a certain page it might automatically connect right usually you wouldn't actually click a button to connect to a websocket I mean it might be after login but we're going to manually show you the difference because if you're not connected you're obviously not going to receive messages right so I want to basically show the differences here but you can say that when you want to open the connection you can actually first check if the connection exists and if it does you actually want to close it first right so we want to close that connection first and then we're going to say because we don't want to just leave that websocket connection open if it already exists and we're going to create a new websocket and it's going to be the protocol WS right now if you had encryption it would be WSS but in our case we're just going to use ws and it's going to be low close and then 3000 is the port so we're going to leave it as localhost 3000 and then we're going to say ws.add event listener and let's first Define our error function right all right so we can just kind of show message we'll just do this show message for pretty much everything and we'll say websocket whoa websocket websocket error right let's do lowercase I think it looks a little bit better all right show message websocket error right and we can pretty much copy this and we're going to say open and it'll be websocket connection established and finally on close well not finally I guess we're gonna have our open or close then we have our message still right so we'll say websocket connection closed and then finally we have our message right so message is the last one so it's on message we actually want to show the message itself right so we can say in here it'll be an argument and it'll be you know a message event and it'll be of type string now you might have an object in here right if you have a user ID things like that in there most of the time you will have an object it's not going to just be a string you'll have an object itself you might have to parse the object that kind of thing but in this particular case we're just going to do a string itself um and then well I guess technically it might just be a string and you need to parse it into an object it's up to you there's different ways to kind of Define this from a typescript standpoint but again by default the string itself is going to be the type of the data on here so MSG we can kind of look at this object real quick but more or less let's look at let's use the types we'll say perceived message and it's going to MSG dot now you can kind of see that it looks similar to an event right like a Dom event in a way it has bubbling it has cancelable it has is trusted origin things like that right so you can stop propagation all sorts of fun stuff time stamp so it is pretty similar to a Dom event but what we need is this data here and that's the data itself that's being sent so we're going to grab that data it's going to be a string and we are going to now um you know receive the data itself and we're going to show it as received data right so again that was the websocket open we're creating the websocket and we're instantiating all of the listeners when the websocket is established so if that was on page load then you would do all of this on page load right now we're also going to create a close event right so we're going to have that close button so it's add event listener click and we're basically going to do the exact same thing as what was in the open except that's all we're going to do so we could actually we could actually have this function separate you know if we really wanted to optimize you know usability here uh you know we'd have a separate function here so let's just say function close connection and it would just be you know this code right here and we could call close connection obviously most of you guys know how to write functions so this is kind of redundant but we'll just put that in there and make it a little bit cleaner all right so now we have our closed connection calling so we close Connection in the open and we also close connection on the close right all right so the last thing that we have to do is actually the send right so we can say WS send dot add event listener and that's going to be click and similar to the other two we're just using our event handler here and we can first check if you know WS exists and if it doesn't we can show message uh you know no websocket connection and we'll just return otherwise we're going to actually grab the Ws input dot value all right and we're going to check you know if not Val we're just going to return because we do not need to we could do this first actually you could just you know you don't even need to check you know if for instance if WS doesn't exist right as you can say else if not WS then you want to show that right so only if Val exists do we even check because otherwise it's not even a big deal because we're not doing anything right so I prefer checking this first uh and then if WS does exist if our socket is connected then we want to do WS dot send and we're going to just send Val which is our input value and then we're going to show message after that and we're going to say sent and we're going to pass in about here so we're going to actually see when we send the value and then we're going to see when we receive the value as well which is going to be an important distinction obviously but it will make a little bit more sense when we open up more than one connection right all right so I think that's pretty much everything I don't believe I messed anything up but we're probably going to find out pretty quick uh let's go ahead and run this thing so npm run Dev all right listening on Port 3000 let's go ahead and pop open localhost 3000 all right so we can pop we can pull up in the I don't think we're gonna need anything really in here but let's go ahead and just have it open just in case in case we see any errors or anything like that but you can see that if we send the message nothing happens we try and send a message then we don't actually get that no websocket connections put down here but it would be nice to actually maybe if we do send it let's uh do one more thing let's go ahead and I forgot a semicolon we're going to say WS input dot value equals empty string right so we want to clear that value all right get a little automatic recompile and we'll pop this back open and again you know we can't send the message closing the connection doesn't do anything but we can open the connection we now have a web connection established websocket connection is established we can send not Tisa it's just send test all right Cent test we receive tests so it went up to the server went through the upgrade process and then back to us right so I went through the clients we only have one client at the moment and it sent us a message yes now if we wanted to open up a second one all right let's go ahead and pop this out and we're going to go to the plus 3000 again and let's shrink this up let's shrink it up to about half size we're going to put kind of put it over here let's make it a little bit bigger there we go all right so now we have two two instances open we only have one connection still right so for instance if we sent another message um test two right we pulled this back up you can see nothing actually happened here but if we open the connection and we send test three you can actually now see that we received the message test three all right and so we don't actually have to you know send a message to receive a message once the connection is open we're receiving now if we wanted to go ahead and send you know ping we get ping over here and pong we get pong back right now if we close a connection we go ahead and say all right you know we're done over here let's go ahead let's close the connection over here right so we close the websocket connection now if we sang something else here let's just say um you know song we can see that we sent song receive song but this connection is closed so we didn't actually get anything over here all right so that client is now done and we're not actually ever going to be receiving data anymore right so again you can see that we can open up as many of these as we want and it's going to actually send them to all of them right so let's go ahead and do that just for fun let's do one more localhost 3000 pop this out and we're going to go ahead open this connection all right then we're going to pull these up and we're going to say you know hello world send message oh we got to open this connection first send message and you see receive message hello world receive message hello world receive message hello world so we have our websockets communicating to all instances we can scale this out probably to about 10 000 before it starts giving us really any issues so again I love the the Ws npm package um socket IO is another option that you could look at but I prefer using this one because it's cleaner and honestly it's everything you need so hope you enjoyed and we'll see you back here soon all right so I hope you guys enjoyed that and I hope it was easy enough to understand there's definitely things that you can kind of take and run with as far as web sockets go because again this was just a basic video setting up a very simple server but again we're probably going to have some follow-up videos that kind of go a little more in depth in some you know some of the topics that we discussed during the video itself so if you have any questions or suggestions feel free to drop them in the comments below make sure you subscribe and also check out our merch store check out our courses online and honestly have fun with development come back see you soon and good luck
Info
Channel: Covalence
Views: 15,828
Rating: undefined out of 5
Keywords: Covalence, Programming, Coding, Software Development, learn to code, online coding bootcamp, intro to react, intro to javascript, intro to typescript, node.js, MySQL, .NET, React.js, free web development tutorials, software development bootcamp, web development, javascript, typescript, mern stack, express, become a programmer, how to become a software developer, programming tutorials, become a full stack developer, software careers, full stack software developer salaries, websockets, ws, npm
Id: Gq7fenbjehs
Channel Id: undefined
Length: 33min 25sec (2005 seconds)
Published: Fri Mar 10 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.