Juana  Wunsch

Juana Wunsch

1590908640

Building Chat Service in Golang and Websockets Backed by Redis

Dependency injection is great technique for decoupling code, making it more stable, testable and robust. The technique consists in designing your code to receive dependencies. In object oriented languages, we usually use the class constructor to do it. So when someone creates an instance of your object, all dependencies will be injected into it. That makes perfect sense as dependencies shouldn’t be part of interface signatures, otherwise all implementations would have to receive the dependencies as parameters for doing their job. But each implementation can have different dependencies based on what they do. Well you get the problem, right?

#redis #websocket #golang #javascript

What is GEEK

Buddha Community

Building Chat Service in Golang and Websockets Backed by Redis
Zachary Palmer

Zachary Palmer

1555901576

CSS Flexbox Tutorial | Build a Chat Application

Creating the conversation sidebar and main chat section

In this article we are going to focus on building a basic sidebar, and the main chat window inside our chat shell. See below.

Chat shell with a fixed width sidebar and expanded chat window

This is the second article in this series. You can check out the previous article for setting up the shell OR you can just check out the chat-shell branch from the following repository.

https://github.com/lyraddigital/flexbox-chat-app.git

Open up the chat.html file. You should have the following HTML.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Chat App</title>
    <link rel="stylesheet" type="text/css" media="screen" href="css/chat.css" />
</head>
<body>
    <div id="chat-container">
    </div>
</body>
</html>

Now inside of the chat-container div add the following HTML.

<div id="side-bar">
</div>
<div id="chat-window">
</div>

Now let’s also add the following CSS under the #chat-container selector in the chat.css file.

#side-bar {
    background: #0048AA;
    border-radius: 10px 0 0 10px;
}
#chat-window {
    background: #999;
    border-radius: 0 10px 10px 0;
}

Now reload the page. You should see the following:-

So what happened? Where is our sidebar and where is our chat window? I expected to see a blue side bar and a grey chat window, but it’s no where to be found. Well it’s all good. This is because we have no content inside of either element, so it can be 0 pixels wide.

Sizing Flex Items

So now that we know that our items are 0 pixels wide, let’s attempt to size them. We’ll attempt to try this first using explicit widths.

Add the following width property to the #side-bar rule, then reload the page.

width: 275px;

Hmm. Same result. It’s still a blank shell. Oh wait I have to make sure the height is 100% too. So we better do that too. Once again add the following property to the #side-bar rule, then reload the page.

height: 100%;

So now we have our sidebar that has grown to be exactly 275 pixels wide, and is 100% high. So that’s it. We’re done right? Wrong. Let me ask you a question. How big is the chat window? Let’s test that by adding some text to it. Try this yourself just add some text. You should see something similar to this.

So as you can see the chat window is only as big as the text that’s inside of it, and it is not next to the side bar. And this makes sense because up until now the chat shell is not a flex container, and just a regular block level element.

So let’s make our chat shell a flex container. Set the following display property for the #chat-window selector. Then reload the page.

display: flex;

So as you can see by the above illustration, we can see it’s now next to the side bar, and not below it. But as you can see currently it’s only as wide as the text that’s inside of it.

But we want it to take up the remaining space of the chat shell. Well we know how to do this, as we did it in the previous article. Set the flex-grow property to 1 on the #chat-window selector. Basically copy and paste the property below and reload the page.

flex-grow: 1;

So now we have the chat window taking up the remaining space of the chat shell. Next, let’s remove the background property, and also remove all text inside the chat-window div if any still exists. You should now see the result below.

But are we done? Technically yes, but before we move on, let’s improve things a little bit.

Understanding the default alignment

If you remember, before we had defined our chat shell to be a flex container, we had to make sure we set the height of the side bar to be 100%. Otherwise it was 0 pixels high, and as a result nothing was displayed. With that said, try removing the height property from the #side-bar selector and see what happens when you reload the page. Yes that’s right, it still works. The height of the sidebar is still 100% high.

So what happened here? Why do we no longer have to worry about setting the height to 100%? Well this is one of the cool things Flexbox gives you for free. By default every flex item will stretch vertically to fill in the entire height of the flex container. We can in fact change this behaviour, and we will see how this is done in a future article.

Setting the size of the side bar properly

So another feature of Flexbox is being able to set the size of a flex item by using the flex-basis property. The flex-basis property allows you to specify an initial size of a flex item, before any growing or shrinking takes place. We’ll understand more about this in an upcoming article.

For now I just want you to understand one important thing. And that is using width to specify the size of the sidebar is not a good idea. Let’s see why.

Say that potentially, if the screen is mobile we want the side bar to now appear across the top of the chat shell, acting like a top bar instead. We can do this by changing the direction flex items can flex inside a flex container. For example, add the following CSS to the #chat-container selector. Then reload the page.

flex-direction: column;

So as you can see we are back to a blank shell. So firstly let’s understand what we actually did here. By setting the flex-direction property to column, we changed the direction of how the flex items flex. By default flex items will flex from left to right. However when we set flex-direction to column, it changes this behaviour forcing flex items to flex from top to bottom instead. On top of this, when the direction of flex changes, the sizing and alignment of flex items changes as well.

When flexing from left to right, we get a height of 100% for free as already mentioned, and then we made sure the side bar was set to be 275 pixels wide, by setting the width property.

However now that we a flexing from top to bottom, the width of the flex item by default would be 100% wide, and you would need to specify the height instead. So try this. Add the following property to the #side-bar selector to set the height of the side bar. Then reload the page.

height: 275px;

Now we are seeing the side bar again, as we gave it a fixed height too. But we still have that fixed width. That’s not what we wanted. We want the side bar (ie our new top bar) here to now be 100% wide. Comment out the width for a moment and reload the page again.

So now we were able to move our side bar so it appears on top instead, acting like a top bar. Which as previously mentioned might be suited for mobile device widths. But to do this we had to swap the value of width to be the value of height. Wouldn’t it be great if this size was preserved regardless of which direction our items are flexing.

Try this, remove all widths and height properties from the #side-bar selector and write the following instead. Then reload the page.

flex-basis: 275px;

As you can see we get the same result. Now remove the flex-direction property from the #chat-container selector. Then once again reload the page.

Once again we are back to our final output. But now we also have the flexibility to easily change the side bar to be a top bar if we need to, by just changing the direction items can flow. Regardless of the direction of flex, the size of our side bar / top bar is preserved.

Conclusion

Ok so once again we didn’t build much, but we did cover a lot of concepts about Flexbox around sizing. 

#css #programming #webdev 

Juana  Wunsch

Juana Wunsch

1590908640

Building Chat Service in Golang and Websockets Backed by Redis

Dependency injection is great technique for decoupling code, making it more stable, testable and robust. The technique consists in designing your code to receive dependencies. In object oriented languages, we usually use the class constructor to do it. So when someone creates an instance of your object, all dependencies will be injected into it. That makes perfect sense as dependencies shouldn’t be part of interface signatures, otherwise all implementations would have to receive the dependencies as parameters for doing their job. But each implementation can have different dependencies based on what they do. Well you get the problem, right?

#redis #websocket #golang #javascript

Loma  Baumbach

Loma Baumbach

1596679140

Redis Transactions & Long-Running Lua Scripts

Redis offers two mechanisms for handling transactions – MULTI/EXEC based transactions and Lua scripts evaluation. Redis Lua scripting is the recommended approach and is fairly popular in usage.

Our Redis™ customers who have Lua scripts deployed often report this error – “BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE”. In this post, we will explain the Redis transactional property of scripts, what this error is about, and why we must be extra careful about it on Sentinel-managed systems that can failover.

Redis Lua Scripts Diagram - ScaleGrid Blog

Transactional Nature of Redis Lua Scripts

Redis “transactions” aren’t really transactions as understood conventionally – in case of errors, there is no rollback of writes made by the script.

Atomicity” of Redis scripts is guaranteed in the following manner:

  • Once a script begins executing, all other commands/scripts are blocked until the script completes. So, other clients either see the changes made by the script or they don’t. This is because they can only execute either before the script or after the script.
  • However, Redis doesn’t do rollbacks, so on an error within a script, any changes already made by the script will be retained and future commands/scripts will see those partial changes.
  • Since all other clients are blocked while the script executes, it is critical that the script is well-behaved and finishes in time.

The ‘lua-time-limit’ Value

It is highly recommended that the script complete within a time limit. Redis enforces this in a weak manner with the ‘lua-time-limit’ value. This is the maximum allowed time (in ms) that the script is allowed to run. The default value is 5 seconds. This is a really long time for CPU-bound activity (scripts have limited access and can’t run commands that access the disk).

However, the script is not killed when it executes beyond this time. Redis starts accepting client commands again, but responds to them with a BUSY error.

If you must kill the script at this point, there are two options available:

  • SCRIPT KILL command can be used to stop a script that hasn’t yet done any writes.
  • If the script has already performed writes to the server and must still be killed, use the SHUTDOWN NOSAVE to shutdown the server completely.

It is usually better to just wait for the script to complete its operation. The complete information on methods to kill the script execution and related behavior are available in the documentation.

#cloud #database #developer #high availability #howto #redis #scalegrid #lua-time-limit #redis diagram #redis master #redis scripts #redis sentinel #redis servers #redis transactions #sentinel-managed #server failures

Using WebSockets with Fastify

Web developers often choose Node.js for writing web backends because of its simple development environment, rich library ecosystem, asynchronous single-threaded nature, and supportive developer community.

We can also use various communication mechanisms to implement our Node.js web backends according to our development requirements. Most development teams choose the HTTP-based RESTful pattern, but some development teams use WebSockets with RESTful endpoints on the same Node.js server to implement real-time, bidirectional communication channels. It helps that popular Node.js web frameworks like Express.js, Fastify, and NestJS offer WebSockets integration via official or third-party plugins.

In this tutorial, I will explain how to enable real-time communication channels in your Fastify-based, RESTful web APIs with the fastify-websocket plugin.

Fastify-WebSocket features

The Fastify-WebSocket plugin lets developers extend Fastify RESTful backends with WebSocket protocol features. This plugin uses the Node.js ws library as the underlying WebSocket server implementation and comes with four excellent features, which I’ll detail below.

Handling WebSocket messages within RESTful handlers

The Fastify-WebSocket plugin doesn’t initiate another HTTP server instance to initiate WebSocket connections. Rather,  it uses the same Fastify server instance by default. Therefore, you can handle WebSocket events within any Fastify GET endpoint handler.

Subscribing to WebSocket client event handlers within endpoints

WebSocket client events — like connection initialization, receiving messages, and connection termination — are always helpful in real-time web application development. The Fastify-WebSocket plugin lets developers subscribe to these client events by exposing the underlying Node.js ws library objects.

Controlling WebSocket connections via Hooks

The Fastify Hooks API helps listen to specific events in the Fastify HTTP routing lifecycle. We can use this feature to validate WebSocket connections before the WebSocket handshake occurs.

TypeScript support

The Fastify-WebSocket library comes with an inbuilt TypeScript definitions file, so you don’t need third-party TypeScript definitions for your TypeScript-based Fastify-WebSocket projects.

Fastify-WebSocket tutorial: Creating a basic WebSocket endpoint

We are going to build several example projects with the Fastify-WebSocket plugin. We will explore all features that you need to build real-time, Fastify-based apps in this tutorial.

First, let’s create a new Fastify project to get started.

Creating a new Fastify project

We need to create a new Node.js module for the sample project before installing the Fastify framework. Enter the following commands to create a new Node.js module:

mkdir fastify-ws
cd fastify-ws

npm init -y  
# or 
yarn init -y

The above command will create a package.json file with some default values for our new project. However, you can also use npm init fastify to scaffold a new project based on a pre-defined template with the create-fastify starter script; we will create a blank project for simplicity.

Next, install the Fastify framework with the following command:

npm install fastify
# or
yarn add fastify

Now, let’s create a GET endpoint with a JSON response. Create a new file named main.js and add the following code:

const fastify = require('fastify')();

fastify.get('/hello', (request, reply) => {
    reply.send({
        message: 'Hello Fastify'
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

Add the following scripts section to the package.json file to define the start script for the Node.js module:

"scripts": {
    "start": "node main.js"
}

Run the above example code with npm start and invoke the GET /hello endpoint with Postman, as shown below:

Testing the sample RESTful endpoint with Postman

Adding WebSocket support to endpoints

Let’s create a WebSocket-enabled endpoint to accept WebSocket client connections. Enter the following command to install the Fastify-WebSocket plugin:

npm install fastify-websocket
# or 
yarn add fastify-websocket

Now, we need to activate the plugin before we define the WebSocket-enabled endpoints. Add the following code right after we initialize the fastify constant:

fastify.register(require('fastify-websocket'));

The above code adds WebSocket support for the Fastify RESTful router. Next, create a new GET endpoint named /hello-ws with the WebSocket support, as shown below.

fastify.get('/hello-ws', { websocket: true }, (connection, req) => {
    connection.socket.on('message', message => {
        connection.socket.send('Hello Fastify WebSockets');
    });
});

The above endpoint definition looks like a typical Fastify endpoint, but it uses an additional { websocket: true } configuration object to allow WebSocket handshakes.

Here is the complete source code after adding the WebSocket endpoint:

const fastify = require('fastify')();
fastify.register(require('fastify-websocket'));

fastify.get('/hello', (request, reply) => {
    reply.send({
        message: 'Hello Fastify'
    });
});

fastify.get('/hello-ws', { websocket: true }, (connection, req) => {
    connection.socket.on('message', message => {
        connection.socket.send('Hello Fastify WebSockets');
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

The above code implements two endpoints: the GET /hello to return a JSON payload, and the GET /hello-ws to accept WebSocket handshakes via the HTTP protocol. Also, when the server receives a new WebSocket message, it returns a greeting message to the particular WebSocket client.

Let’s test the above WebSocket endpoint.

Testing our basic WebSocket endpoint with Postman

Typically, developers write client applications to test their WebSocket server implementations, but Postman lets you check any WebSocket connection without writing code.

Open a new WebSocket testing tab in Postman by selecting the WebSocket Request menu item from the New main menu. Connect to the WebSocket endpoint and send a message, as shown below.

Testing the sample WebSocket endpoint with Postman

As shown, you will get a greeting message from the WebSocket server for each message you send. Here, we need to connect to the server using the WebSocket protocol URL; i.e., we could use the following URL format to establish a WebSocket connection via the GET /hello-ws endpoint:

ws://localhost:3000/hello-ws

If you are connecting to your production server via a TLS connection, you need to use wss instead of ws, as we’ll use https instead of http.

Using WebSocket client event handlers

The WebSocket concept is a solution for managing a real-time, bidirectional connection between a web server and clients. If you use WebSockets to build a group chat application, you typically need to know when a new client connects and disconnects. The Fastify-WebSocket library lets you subscribe to these events via the underlying ws library implementation.

Update the current GET /hello-ws endpoint implementation with the following code snippet to experiment with client event handlers:

fastify.get('/hello-ws', { websocket: true }, (connection, req) => {
    // Client connect
    console.log('Client connected');
    // Client message
    connection.socket.on('message', message => {
        console.log(`Client message: ${message}`);
    });
    // Client disconnect
    connection.socket.on('close', () => {
        console.log('Client disconnected');
    });
});

When the WebSocket handshake is successful, the plugin invokes the WebSocket endpoint handler , which we can use to detect the client connection event.

As shown above, we can use the close event handler to identify WebSocket client disconnections. The message event handler gets invoked for each incoming client message.

Try to open several Postman WebSocket testing tabs and send some messages  —  you will see client events on the terminal, as shown below.

WebSocket client events in the terminal

We haven’t yet written any code yet to store client connection details, but we will discuss it later in this tutorial when we build a real-time chat application example.

Fastify-WebSocket tutorial: Creating multiple WebSocket endpoints using the same server

The Fastify-WebSocket plugin is very flexible. It lets you make more than one WebSocket endpoint via route definitions.

You can create any number of WebSocket-enabled RESTful endpoints by adding the { websocket: true } configuration object to the route definition. Look at the following example:

const fastify = require('fastify')();
fastify.register(require('fastify-websocket'));

fastify.get('/digits', { websocket: true }, (connection, req) => {
    let timer = setInterval(() => {
        connection.socket.send(randomDigit(1, 10).toString());
    }, 1000);
    connection.socket.on('close', () => {
        clearInterval(timer);
    });
});

fastify.get('/letters', { websocket: true }, (connection, req) => {
    let timer = setInterval(() => {
        connection.socket.send(randomLetter());
    }, 1000);
    connection.socket.on('close', () => {
        clearInterval(timer);
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

function randomDigit(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}

function randomLetter() {
    return 'abcdefghijklmnopqrstuvwxyz'[randomDigit(1, 26)];
}

The above code snippet implements two WebSocket endpoints:

  • GET /digits: This WebSocket endpoint sends random digits once connected
  • GET /letters: This WebSocket endpoint sends random English letters once connected

You can test the above WebSocket endpoints simultaneously with Postman by connecting to both, as shown below.

Testing multiple WebSocket endpoints with Postman

Similarly, you can implement more WebSocket endpoints on the same Fastify server, and you can accept WebSocket connections via any GET endpoint by registering a WebSocket-enabled GET endpoint to the /* route.

Configuring the WebSocket server

The ws Node.js library comes into play here again to handle WebSocket data transmissions. Its WebSocket implementation accepts a configuration object with several properties, so the fastify-websocket plugin also accepts those configuration properties.

For example, we can change the maximum allowed message size via the maxPayload property, as shown below.

fastify.register(require('fastify-websocket'), {
    options: {
        maxPayload: 10 // in bytes
    }
});

You can browse all supported data transmission configuration options from the ws module documentation.

Validating WebSocket connection initializations with Hooks

In some scenarios, we may need to accept only specific WebSocket connection requests according to a set of validation rules. For example, we can allow WebSocket connections by checking the URL query parameters or HTTP headers.

We can conditionally accept or reject incoming WebSocket connections with the prevValidation Hook. The following server-side code allows WebSocket clients that connect to the server with the username query parameter in the URL:

const fastify = require('fastify')();
fastify.register(require('fastify-websocket'));

fastify.addHook('preValidation', async (request, reply) => {
    if(!request.query.username) {
        reply.code(403).send('Connection rejected');
    }
});

fastify.get('/*', { websocket: true }, (connection, req) => {
    connection.socket.send(`Hello ${req.query.username}!`);
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

The above code snippet seeks WebSocket connections from any GET endpoint with the wildcard routing syntax (/*), but it conditionally accepts connections if the username query parameter is present. For example, you can’t establish a WebSocket connection with the following URLs:

ws://localhost:3000
ws://localhost:3000/ws
ws://localhost:3000/hello-ws

But you can establish a WebSocket connection and receive a greeting message with the following URLs:

ws://localhost:3000?username=Fastify
ws://localhost:3000/ws?username=Developer
ws://localhost:3000/hello-ws?username=Nodejs
ws://localhost:3000/hello-ws?username=Nodejs&anotherparam=10

Besides, you can validate WebSocket connection initializations by checking WebSocket handshake headers, too, via the request.headers property.

Handling HTTP responses and WebSockets in the same route

Assume that if someone visits a WebSocket endpoint from the web browser, you need to reply with an HTTP response. Then, we need to return that particular HTTP response if the endpoint receives a normal HTTP request, but we still need to perform WebSocket handshakes to accept incoming WebSocket connections.

We can handle both protocols in the same endpoint by using Fastify’s full declaration syntax, as shown below.

fastify.route({
    method: 'GET',
    url: '/hello',
    handler: (req, reply) => {
        // HTTP response
        reply.send({ message: 'Hello Fastify' });
    },
    wsHandler: (conn, req) => {
        // WebSocket message
        conn.socket.send('Hello Fastify WebSockets');
    }
});

Here, we make HTTP responses via the handler callback and communicate with WebSocket clients via the wsHandler callback. Both operations happen within the GET /hello endpoint.

Fastify-WebSocket tutorial: Building a simple chat app with fastify-websocket

We’ve discussed almost all of the features the fastify-websocket plugin provides, so it’s time to build a simple group chat application by using those features.

This chat app will let anyone enter a group conversation by entering a username. Once a user enters the username, the chat app lets the particular user post a message for all users.

Let’s keep it simple and build this application with vanilla JavaScript and plain HTML.

Setting up the fastify-static plugin

First, we need to install the fastify-static plugin to enable the static file serving feature to serve the chat application frontend. Install the plugin with the following command:

npm install fastify-static
# or 
yarn add fastify-static

Next, add the following code to your main.js file:

const fastify = require('fastify')();
const path = require('path');

fastify.register(require('fastify-websocket'));
fastify.register(require('fastify-static'), {
    root: path.join(__dirname, 'www')
});

fastify.addHook('preValidation', async (request, reply) => {
    if(request.routerPath == '/chat' && !request.query.username) {
        reply.code(403).send('Connection rejected');
    }
});

fastify.get('/chat', { websocket: true }, (connection, req) => {
    // New user
    broadcast({
        sender: '__server',
        message: `${req.query.username} joined`
    });
    // Leaving user
    connection.socket.on('close', () => {
        broadcast({
            sender: '__server',
            message: `${req.query.username} left`
        });
    });
   // Broadcast incoming message
    connection.socket.on('message', (message) => {
        message = JSON.parse(message.toString());
        broadcast({
            sender: req.query.username,
            ...message
        });
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

function broadcast(message) {
    for(let client of fastify.websocketServer.clients) {
        client.send(JSON.stringify(message));
    }
}

The above server-side implementation contains a static file server to serve the frontend application resources. It also handles the WebSocket server-side events of the chat application, i.e., when a new chat client tries to establish a connection, it conditionally accepts the connection by checking the existence of the username query parameter. Moreover, it also notifies all chat clients when:

  • A new user joins the conversation
  • A user sends a message from the application frontend
  • An existing user leaves the conversation

All unique WebSocket client connection references are stored in the fastify.websocketServer.clients Set_, so we can loop through it and send a message to all connected chat users. This action is known as broadcasting in WebSocket-based applications; we’ve implemented it inside the broadcast function.

Before developing the frontend, you also can test the WebSocket endpoint with Postman. Try to open several WebSocket testing tabs and connect with the WebSocket endpoint by providing different usernames.

Building the chat app frontend

Let’s build the chat application frontend. Create a directory named www, and inside the project directory create index.html, where you’ll add the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Chat</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="description" content="" />
    <style>
        html, body {
            margin: 0;
            padding: 0;
        }

        * {
            box-sizing: border-box;
            font-family: Arial;
        }
#chat {
            width: 100vw;
            height: 100vh;
            padding: 12px;
        }
#chat div {
            padding: 4px 0px;
        }
#chat div b {
            color: #555;
        }
input[type=text] {
            position: fixed;
            bottom: 10px;
            left: 12px;
            outline: none;
            width: 400px;
            border: #555 solid 1px;
            font-size: 14px;
            padding: 4px;
        }
    </style>
</head>
<body>
    <div id="chat"></div>
    <input id="message" type="text" autofocus/>
<script>
        let _ws = null;
init();
function init() {
            let username = getUsername();
if(!username) {
                sessionStorage.setItem('username', prompt('Enter username'))
                username = getUsername();
            }
if(!username) {
                init();
            }
_ws = new WebSocket(`ws://${window.location.host}/chat?username=${username}`);
_ws.onmessage = (message) => {
                message = JSON.parse(message.data);
                appendMessage(message);
            };
document.getElementById('message')
                .onkeypress = (evt) => {
                    if(evt.key == 'Enter') {
                        _ws.send(JSON.stringify({
                            message: evt.target.value
                        }));
                        evt.target.value = '';
                    }
                };
        }
function getUsername() {
            return sessionStorage.username;
        }
function appendMessage(message) {
            document.getElementById('chat').innerHTML +=
            `
            <div>
                <b>${message.sender}:&nbsp;</b>
                ${message.message}
            </div>
`
        }
    </script>
</body>
</html>

The above code implements a minimal frontend for the chat application backend that we just built with the Fastify-WebSocket plugin. Start the Fastify server with the npm start (or yarn start) command and go to the following URL to access the chat application:

http://localhost:3000

Try to open multiple browser windows and test the application, as shown below.

Demonstrating a WebSocket chat application using two Chrome windows

You can download the full source code from my GitHub repository.

Fastify-WebSocket vs. ws vs. Fastify-ws

The Fastify-WebSocket plugin is a great solution to add WebSocket endpoints to an existing Fastify-based RESTful web service. And, if you’re planning to build a real-time web application like our demo chat app, using fastify, fastify-websocket, and fastify-static Node.js modules gives your project an instant kickstart.

However, if you need more control over your WebSocket server lifecycle, events, and configuration, using the ws library directly is a good idea. The Fastify-WebSocket plugin wraps the ws library’s functionality to offer you an abstract Fastify plugin. However, the plugin is flexible enough for any general purpose, real-time application because it offers a direct way to subscribe to every necessary WebSocket client event.

There is also the fastify-ws third-party plugin for adding WebSocket plugin for Fastify-based web services, but, unfortunately, it’s not actively developed and doesn’t support the features that the fastify-websocket plugin offers (especially adding WebSocket support to a specific route).

Quick guide to organizing Fastify-WebSocket code

We’ve worked with two different protocols in this post: RESTful HTTP and WebSockets. The RESTful pattern follows a stateless, uni-directional, and request-response-based communication strategy, while the WebSocket concept is asynchronous and a typically stateful communication mechanism. As a result, we need to carefully organize the code to reduce its complexity and achieve better maintainability factors.

Consider using the following pointers for organizing your Fastify-WebSocket-based codebases:

  • Use an MVC-like project structure to enhance the maintainability factors by separating routes, handlers, controllers, and helper modules
  • If your WebSocket event handling logic grows, write separate message handler functions instead of anonymous functions  (and  move them to separate modules if needed)
  • Try not to mix typical RESTful endpoints with WebSocket-enabled endpoints  —  isolate WebSocket endpoints into a module if possible
    • For example, you can create a file named chat.js and place the WebSocket endpoint and event handlers of a real-time chat module
  • Try to apply the DRY programming principle and create shared functions for repetitive code in event handlers
    • For example, consider the broadcast function in the chat app we built together!

Conclusion

In this tutorial, we learned how to use the Fastify-WebSocket plugin with several practical examples.

The Fastify development team developed this plugin as a wrapper for the ws library, but it’s most useful because it lets us make customizations that we often need. This plugin’s goal is to support Fastify developers in adding WebSocket-enabled endpoints into RESTful web services with the same Fastify routing syntax.

Therefore, developers can easily extend their RESTful app backends with real-time web app modules, such as inbuilt chat systems, monitoring dashboards, and more. Its best advantage is that you can use only one network port for all WebSocket and HTTP connections — making your authentication strategy simple.

The Fastify-WebSocket plugin project is actively developed, provides good developer support, and offers inbuilt TypeScript support  —  so we can use it in our Fastify projects without a doubt.

Source: https://blog.logrocket.com/using-websockets-with-fastify/

#websocket #fastify #rest #http 

Uso de WebSockets con Fastify

Los desarrolladores web a menudo eligen Node.js para escribir backends web debido a su entorno de desarrollo simple, rico ecosistema de biblioteca, naturaleza asíncrona de subproceso único y comunidad de desarrolladores de apoyo.

También podemos usar varios mecanismos de comunicación para implementar nuestros backends web Node.js de acuerdo con nuestros requisitos de desarrollo. La mayoría de los equipos de desarrollo eligen el patrón RESTful basado en HTTP, pero algunos equipos de desarrollo usan WebSockets con extremos RESTful en el mismo servidor Node.js para implementar canales de comunicación bidireccionales en tiempo real. Ayuda que los marcos web populares de Node.js como Express.js, Fastify y NestJS ofrezcan integración de WebSockets a través de complementos oficiales o de terceros.

En este tutorial, explicaré cómo habilitar canales de comunicación en tiempo real en sus API web RESTful basadas en Fastify con el fastify-websocket complemento .

Características de Fastify-WebSocket

El complemento Fastify-WebSocket permite a los desarrolladores ampliar los backends Fastify RESTful con características del protocolo WebSocket. Este complemento utiliza la biblioteca Node.js ws como la implementación del servidor WebSocket subyacente y viene con cuatro características excelentes, que detallaré a continuación.

Manejo de mensajes WebSocket dentro de controladores RESTful

El complemento Fastify-WebSocket no inicia otra instancia de servidor HTTP para iniciar conexiones WebSocket. Más bien, utiliza la misma instancia de servidor Fastify de forma predeterminada. Por lo tanto, puede manejar eventos de WebSocket dentro de cualquier controlador de GETpunto final de Fastify.

Suscribirse a controladores de eventos de cliente WebSocket dentro de puntos finales

Los eventos del cliente de WebSocket, como la inicialización de la conexión, la recepción de mensajes y la finalización de la conexión, siempre son útiles en el desarrollo de aplicaciones web en tiempo real. El complemento Fastify-WebSocket permite a los desarrolladores suscribirse a estos eventos de cliente al exponer los objetos de biblioteca subyacentes de Node.js ws.

Control de conexiones WebSocket a través de Hooks

La API Fastify Hooks ayuda a escuchar eventos específicos en el ciclo de vida del enrutamiento HTTP de Fastify. Podemos usar esta función para validar las conexiones de WebSocket antes de que se produzca el protocolo de enlace de WebSocket.

Compatibilidad con mecanografiado

La biblioteca Fastify-WebSocket viene con un archivo de definiciones de TypeScript incorporado, por lo que no necesita definiciones de TypeScript de terceros para sus proyectos de Fastify-WebSocket basados ​​en TypeScript.

Tutorial de Fastify-WebSocket: Creación de un punto de conexión WebSocket básico

Vamos a construir varios proyectos de ejemplo con el complemento Fastify-WebSocket. Exploraremos todas las funciones que necesita para crear aplicaciones basadas en Fastify en tiempo real en este tutorial.

Primero, creemos un nuevo proyecto Fastify para comenzar.

Creación de un nuevo proyecto Fastify

Necesitamos crear un nuevo módulo Node.js para el proyecto de muestra antes de instalar el marco Fastify. Ingrese los siguientes comandos para crear un nuevo módulo Node.js:

mkdir fastify-ws
cd fastify-ws

npm init -y  
# or 
yarn init -y

El comando anterior creará un package.jsonarchivo con algunos valores predeterminados para nuestro nuevo proyecto. Sin embargo, también puede usar npm init fastifypara montar un nuevo proyecto basado en una plantilla predefinida con el script de inicio create-fastify ; crearemos un proyecto en blanco por simplicidad.

A continuación, instale el marco Fastify con el siguiente comando:

npm install fastify
# or
yarn add fastify

Ahora, creemos un GETpunto final con una respuesta JSON. Cree un nuevo archivo llamado main.jsy agregue el siguiente código:

const fastify = require('fastify')();

fastify.get('/hello', (request, reply) => {
    reply.send({
        message: 'Hello Fastify'
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

Agregue la siguiente sección de scripts al package.jsonarchivo para definir el startscript para el módulo Node.js:

"scripts": {
    "start": "node main.js"
}

Ejecute el código de ejemplo anterior npm starte invoque el GET /hellopunto final con Postman, como se muestra a continuación:

Adición de compatibilidad con WebSocket a puntos finales

Vamos a crear un punto final habilitado para WebSocket para aceptar conexiones de clientes WebSocket. Ingrese el siguiente comando para instalar el complemento Fastify-WebSocket:

npm install fastify-websocket
# or 
yarn add fastify-websocket

Ahora, debemos activar el complemento antes de definir los puntos finales habilitados para WebSocket. Agregue el siguiente código justo después de inicializar la fastifyconstante:

fastify.register(require('fastify-websocket'));

El código anterior agrega compatibilidad con WebSocket para el enrutador Fastify RESTful. A continuación, cree un nuevo GETpunto final denominado /hello-wscon la compatibilidad con WebSocket, como se muestra a continuación.

fastify.get('/hello-ws', { websocket: true }, (connection, req) => {
    connection.socket.on('message', message => {
        connection.socket.send('Hello Fastify WebSockets');
    });
});

La definición de punto final anterior parece un punto final típico de Fastify, pero utiliza un { websocket: true }objeto de configuración adicional para permitir los protocolos de enlace de WebSocket.

Aquí está el código fuente completo después de agregar el punto final de WebSocket:

const fastify = require('fastify')();
fastify.register(require('fastify-websocket'));

fastify.get('/hello', (request, reply) => {
    reply.send({
        message: 'Hello Fastify'
    });
});

fastify.get('/hello-ws', { websocket: true }, (connection, req) => {
    connection.socket.on('message', message => {
        connection.socket.send('Hello Fastify WebSockets');
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

El código anterior implementa dos puntos finales: el GET /hellode devolver una carga JSON y el GET /hello-wsde aceptar protocolos de enlace WebSocket a través del protocolo HTTP. Además, cuando el servidor recibe un nuevo mensaje WebSocket, devuelve un mensaje de saludo al cliente WebSocket en particular.

Probemos el punto final de WebSocket anterior.

Probando nuestro punto final básico de WebSocket con Postman

Por lo general, los desarrolladores escriben aplicaciones cliente para probar sus implementaciones de servidor WebSocket, pero Postman le permite verificar cualquier conexión WebSocket sin escribir código.

Abra una nueva pestaña de prueba de WebSocket en Postman seleccionando el elemento de menú Solicitud de WebSocket en el menú principal Nuevo . Conéctese al extremo de WebSocket y envíe un mensaje, como se muestra a continuación.

Como se muestra, recibirá un mensaje de saludo del servidor WebSocket por cada mensaje que envíe. Aquí, necesitamos conectarnos al servidor usando la URL del protocolo WebSocket; es decir, podríamos usar el siguiente formato de URL para establecer una conexión WebSocket a través del GET /hello-wspunto final:

ws://localhost:3000/hello-ws

Si se está conectando a su servidor de producción a través de una conexión TLS , debe usar wssen lugar de ws, ya que usaremos httpsen lugar de http.

Uso de controladores de eventos de cliente WebSocket

El concepto WebSocket es una solución para administrar una conexión bidireccional en tiempo real entre un servidor web y los clientes. Si usa WebSockets para crear una aplicación de chat grupal, generalmente necesita saber cuándo se conecta y desconecta un nuevo cliente. La biblioteca Fastify-WebSocket le permite suscribirse a estos eventos a través de la implementación de la biblioteca ws subyacente .

Actualice la GET /hello-wsimplementación del punto final actual con el siguiente fragmento de código para experimentar con los controladores de eventos del cliente:

fastify.get('/hello-ws', { websocket: true }, (connection, req) => {
    // Client connect
    console.log('Client connected');
    // Client message
    connection.socket.on('message', message => {
        console.log(`Client message: ${message}`);
    });
    // Client disconnect
    connection.socket.on('close', () => {
        console.log('Client disconnected');
    });
});

Cuando el protocolo de enlace de WebSocket tiene éxito, el complemento invoca el controlador de punto final de WebSocket, que podemos usar para detectar el evento de conexión del cliente.

Como se muestra arriba, podemos usar el closecontrolador de eventos para identificar las desconexiones del cliente WebSocket. El messagecontrolador de eventos se invoca para cada mensaje de cliente entrante.

Intente abrir varias pestañas de prueba de Postman WebSocket y envíe algunos mensajes; verá los eventos del cliente en la terminal, como se muestra a continuación.

Todavía no hemos escrito ningún código para almacenar los detalles de la conexión del cliente, pero lo discutiremos más adelante en este tutorial cuando construyamos un ejemplo de aplicación de chat en tiempo real.

Tutorial de Fastify-WebSocket: Creación de múltiples puntos finales de WebSocket usando el mismo servidor

El complemento Fastify-WebSocket es muy flexible. Le permite crear más de un punto final de WebSocket a través de definiciones de ruta.

Puede crear cualquier cantidad de puntos finales RESTful habilitados para WebSocket agregando el { websocket: true }objeto de configuración a la definición de ruta. Mira el siguiente ejemplo:

const fastify = require('fastify')();
fastify.register(require('fastify-websocket'));

fastify.get('/digits', { websocket: true }, (connection, req) => {
    let timer = setInterval(() => {
        connection.socket.send(randomDigit(1, 10).toString());
    }, 1000);
    connection.socket.on('close', () => {
        clearInterval(timer);
    });
});

fastify.get('/letters', { websocket: true }, (connection, req) => {
    let timer = setInterval(() => {
        connection.socket.send(randomLetter());
    }, 1000);
    connection.socket.on('close', () => {
        clearInterval(timer);
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

function randomDigit(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}

function randomLetter() {
    return 'abcdefghijklmnopqrstuvwxyz'[randomDigit(1, 26)];
}

El fragmento de código anterior implementa dos extremos de WebSocket:

  • GET /digits: Este punto final de WebSocket envía dígitos aleatorios una vez conectado
  • GET /letters: Este punto final de WebSocket envía letras aleatorias en inglés una vez conectado

Puede probar los puntos finales de WebSocket anteriores simultáneamente con Postman conectándose a ambos, como se muestra a continuación.

De manera similar, puede implementar más puntos finales WebSocket en el mismo servidor Fastify y puede aceptar conexiones WebSocket a través GETde cualquier punto final registrando un punto final habilitado GETpara WebSocket en la /*ruta.

Configuración del servidor WebSocket

La biblioteca ws Node.js vuelve a entrar en juego aquí para manejar las transmisiones de datos de WebSocket. Su implementación WebSocket acepta un objeto de configuración con varias propiedades, por lo que el complemento fastify-websocket también acepta esas propiedades de configuración.

Por ejemplo, podemos cambiar el tamaño de mensaje máximo permitido a través de la maxPayloadpropiedad, como se muestra a continuación.

fastify.register(require('fastify-websocket'), {
    options: {
        maxPayload: 10 // in bytes
    }
});

Puede examinar todas las opciones de configuración de transmisión de datos admitidas en la documentación del módulo ws .

Validación de inicializaciones de conexión WebSocket con Hooks

En algunos escenarios, es posible que debamos aceptar solo solicitudes de conexión de WebSocket específicas de acuerdo con un conjunto de reglas de validación. Por ejemplo, podemos permitir conexiones WebSocket comprobando los parámetros de consulta de URL o los encabezados HTTP.

Podemos aceptar o rechazar condicionalmente las conexiones entrantes de WebSocket con prevValidationHook. El siguiente código del lado del servidor permite que los clientes de WebSocket se conecten al servidor con el usernameparámetro de consulta en la URL:

const fastify = require('fastify')();
fastify.register(require('fastify-websocket'));

fastify.addHook('preValidation', async (request, reply) => {
    if(!request.query.username) {
        reply.code(403).send('Connection rejected');
    }
});

fastify.get('/*', { websocket: true }, (connection, req) => {
    connection.socket.send(`Hello ${req.query.username}!`);
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

El fragmento de código anterior busca conexiones WebSocket desde cualquier GETextremo con la sintaxis de enrutamiento comodín ( /*), pero acepta conexiones de forma condicional si el usernameparámetro de consulta está presente. Por ejemplo, no puede establecer una conexión WebSocket con las siguientes URL:

ws://localhost:3000
ws://localhost:3000/ws
ws://localhost:3000/hello-ws

Pero puede establecer una conexión WebSocket y recibir un mensaje de bienvenida con las siguientes URL:

ws://localhost:3000?username=Fastify
ws://localhost:3000/ws?username=Developer
ws://localhost:3000/hello-ws?username=Nodejs
ws://localhost:3000/hello-ws?username=Nodejs&anotherparam=10

Además, puede validar las inicializaciones de conexión de WebSocket comprobando también los encabezados de protocolo de enlace de WebSocket a través de la request.headerspropiedad.

Manejo de respuestas HTTP y WebSockets en la misma ruta

Suponga que si alguien visita un punto final de WebSocket desde el navegador web, debe responder con una respuesta HTTP. Luego, debemos devolver esa respuesta HTTP en particular si el punto final recibe una solicitud HTTP normal, pero aún debemos realizar los protocolos de enlace WebSocket para aceptar las conexiones WebSocket entrantes.

Podemos manejar ambos protocolos en el mismo punto final usando la sintaxis de declaración completa de Fastify , como se muestra a continuación.

fastify.route({
    method: 'GET',
    url: '/hello',
    handler: (req, reply) => {
        // HTTP response
        reply.send({ message: 'Hello Fastify' });
    },
    wsHandler: (conn, req) => {
        // WebSocket message
        conn.socket.send('Hello Fastify WebSockets');
    }
});

Aquí, hacemos respuestas HTTP a través de la handlerdevolución de llamada y nos comunicamos con los clientes de WebSocket a través de la wsHandlerdevolución de llamada. Ambas operaciones ocurren dentro del GET /hellopunto final.

Tutorial de Fastify-WebSocket: creación de una aplicación de chat sencilla con fastify-websocket

Hemos discutido casi todas las funciones que proporciona el complemento fastify-websocket, por lo que es hora de crear una aplicación de chat grupal simple usando esas funciones.

Esta aplicación de chat permitirá que cualquiera ingrese a una conversación grupal ingresando un nombre de usuario. Una vez que un usuario ingresa el nombre de usuario, la aplicación de chat le permite publicar un mensaje para todos los usuarios.

Mantengámoslo simple y construyamos esta aplicación con JavaScript estándar y HTML simple.

Configuración del complemento fastify-static

Primero, necesitamos instalar el complemento fastify-static para habilitar la función de servicio de archivos estáticos para servir la interfaz de la aplicación de chat. Instale el complemento con el siguiente comando:

npm install fastify-static
# or 
yarn add fastify-static

A continuación, agregue el siguiente código a su main.jsarchivo:

const fastify = require('fastify')();
const path = require('path');

fastify.register(require('fastify-websocket'));
fastify.register(require('fastify-static'), {
    root: path.join(__dirname, 'www')
});

fastify.addHook('preValidation', async (request, reply) => {
    if(request.routerPath == '/chat' && !request.query.username) {
        reply.code(403).send('Connection rejected');
    }
});

fastify.get('/chat', { websocket: true }, (connection, req) => {
    // New user
    broadcast({
        sender: '__server',
        message: `${req.query.username} joined`
    });
    // Leaving user
    connection.socket.on('close', () => {
        broadcast({
            sender: '__server',
            message: `${req.query.username} left`
        });
    });
   // Broadcast incoming message
    connection.socket.on('message', (message) => {
        message = JSON.parse(message.toString());
        broadcast({
            sender: req.query.username,
            ...message
        });
    });
});

fastify.listen({ port: 3000 }, (err, address) => {
    if(err) {
        console.error(err);
        process.exit(1);
    }
    console.log(`Server listening at: ${address}`);
});

function broadcast(message) {
    for(let client of fastify.websocketServer.clients) {
        client.send(JSON.stringify(message));
    }
}

La implementación del lado del servidor anterior contiene un servidor de archivos estático para servir los recursos de la aplicación frontend. También maneja los eventos del lado del servidor WebSocket de la aplicación de chat, es decir, cuando un nuevo cliente de chat intenta establecer una conexión, acepta condicionalmente la conexión comprobando la existencia del usernameparámetro de consulta. Además, también notifica a todos los clientes de chat cuando:

  • Un nuevo usuario se une a la conversación.
  • Un usuario envía un mensaje desde la interfaz de la aplicación
  • Un usuario existente abandona la conversación.

Todas las referencias únicas de conexión del cliente WebSocket se almacenan en el fastify.websocketServer.clients Set_ , por lo que podemos recorrerlo y enviar un mensaje a todos los usuarios de chat conectados. Esta acción se conoce como transmisión en aplicaciones basadas en WebSocket; lo hemos implementado dentro de la broadcastfunción.

Antes de desarrollar la interfaz, también puede probar el punto final de WebSocket con Postman. Intente abrir varias pestañas de prueba de WebSocket y conéctese con el extremo de WebSocket proporcionando diferentes nombres de usuario.

Creación de la interfaz de la aplicación de chat

Construyamos la interfaz de la aplicación de chat. Crea un directorio llamado www, y dentro del directorio del proyecto create index.html, donde agregarás el siguiente código:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Chat</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="description" content="" />
    <style>
        html, body {
            margin: 0;
            padding: 0;
        }

        * {
            box-sizing: border-box;
            font-family: Arial;
        }
#chat {
            width: 100vw;
            height: 100vh;
            padding: 12px;
        }
#chat div {
            padding: 4px 0px;
        }
#chat div b {
            color: #555;
        }
input[type=text] {
            position: fixed;
            bottom: 10px;
            left: 12px;
            outline: none;
            width: 400px;
            border: #555 solid 1px;
            font-size: 14px;
            padding: 4px;
        }
    </style>
</head>
<body>
    <div id="chat"></div>
    <input id="message" type="text" autofocus/>
<script>
        let _ws = null;
init();
function init() {
            let username = getUsername();
if(!username) {
                sessionStorage.setItem('username', prompt('Enter username'))
                username = getUsername();
            }
if(!username) {
                init();
            }
_ws = new WebSocket(`ws://${window.location.host}/chat?username=${username}`);
_ws.onmessage = (message) => {
                message = JSON.parse(message.data);
                appendMessage(message);
            };
document.getElementById('message')
                .onkeypress = (evt) => {
                    if(evt.key == 'Enter') {
                        _ws.send(JSON.stringify({
                            message: evt.target.value
                        }));
                        evt.target.value = '';
                    }
                };
        }
function getUsername() {
            return sessionStorage.username;
        }
function appendMessage(message) {
            document.getElementById('chat').innerHTML +=
            `
            <div>
                <b>${message.sender}:&nbsp;</b>
                ${message.message}
            </div>
`
        }
    </script>
</body>
</html>

El código anterior implementa una interfaz mínima para el backend de la aplicación de chat que acabamos de crear con el complemento Fastify-WebSocket. Inicie el servidor Fastify con el comando npm start(o yarn start) y vaya a la siguiente URL para acceder a la aplicación de chat:

http://localhost:3000

Intente abrir varias ventanas del navegador y pruebe la aplicación, como se muestra a continuación.

Puede descargar el código fuente completo desde mi repositorio de GitHub .

Fastify-WebSocket frente a ws frente a Fastify-ws

El complemento Fastify-WebSocket es una excelente solución para agregar puntos finales de WebSocket a un servicio web RESTful basado en Fastify existente. Y, si planea crear una aplicación web en tiempo real como nuestra aplicación de chat de demostración, el uso de los módulos fastify, fastify-websocket y fastify-static Node.js le da a su proyecto un impulso instantáneo.

Sin embargo, si necesita más control sobre el ciclo de vida, los eventos y la configuración de su servidor WebSocket, usar la biblioteca ws directamente es una buena idea. El complemento Fastify-WebSocket envuelve la funcionalidad de la biblioteca ws para ofrecerle un complemento Fastify abstracto. Sin embargo, el complemento es lo suficientemente flexible para cualquier aplicación en tiempo real de propósito general porque ofrece una forma directa de suscribirse a todos los eventos necesarios del cliente WebSocket.

También existe el complemento de terceros fastify-ws para agregar el complemento WebSocket para los servicios web basados ​​en Fastify, pero, desafortunadamente, no está desarrollado activamente y no es compatible con las características que ofrece el complemento fastify-websocket (especialmente agregar compatibilidad con WebSocket para una ruta específica).

Guía rápida para organizar el código Fastify-WebSocket

Hemos trabajado con dos protocolos diferentes en esta publicación: RESTful HTTP y WebSockets. El patrón RESTful sigue una estrategia de comunicación sin estado, unidireccional y basada en solicitudes y respuestas, mientras que el concepto de WebSocket es asincrónico y un mecanismo de comunicación típicamente con estado. Como resultado, necesitamos organizar cuidadosamente el código para reducir su complejidad y lograr mejores factores de mantenibilidad.

Considere usar los siguientes indicadores para organizar sus bases de código basadas en Fastify-WebSocket:

  • Use una estructura de proyecto similar a MVC para mejorar los factores de mantenimiento al separar las rutas, los controladores, los controladores y los módulos auxiliares.
  • Si su lógica de manejo de eventos WebSocket crece, escriba funciones de manejo de mensajes separadas en lugar de funciones anónimas (y muévalas a módulos separados si es necesario)
  • Trate de no mezclar puntos finales RESTful típicos con puntos finales habilitados para WebSocket; aísle los puntos finales WebSocket en un módulo si es posible.
    • Por ejemplo, puede crear un archivo llamado chat.jsy colocar el punto final de WebSocket y los controladores de eventos de un módulo de chat en tiempo real.
  • Intente aplicar el principio de programación DRY y cree funciones compartidas para código repetitivo en controladores de eventos.
    • Por ejemplo, considere la broadcastfunción en la aplicación de chat que construimos juntos.

Conclusión

En este tutorial, aprendimos a usar el complemento Fastify-WebSocket con varios ejemplos prácticos.

El equipo de desarrollo de Fastify desarrolló este complemento como un envoltorio para la biblioteca ws, pero es más útil porque nos permite realizar las personalizaciones que a menudo necesitamos. El objetivo de este complemento es ayudar a los desarrolladores de Fastify a agregar puntos finales habilitados para WebSocket en los servicios web RESTful con la misma sintaxis de enrutamiento de Fastify.

Por lo tanto, los desarrolladores pueden extender fácilmente sus backends de aplicaciones RESTful con módulos de aplicaciones web en tiempo real, como sistemas de chat incorporados, paneles de monitoreo y más. Su mejor ventaja es que puede usar solo un puerto de red para todas las conexiones WebSocket y HTTP, lo que simplifica su estrategia de autenticación.

El proyecto del complemento Fastify-WebSocket se desarrolla activamente, brinda un buen soporte para desarrolladores y ofrece soporte TypeScript incorporado, por lo que podemos usarlo en nuestros proyectos Fastify sin duda.

Fuente: https://blog.logrocket.com/using-websockets-with-fastify/ 

#websocket #fastify #rest #http