Nest provides a variety of transporters out-of-the-box, as well as an API allowing developers to build new custom transport strategies. Transporters enable you to connect components over a network using a pluggable communications layer and a very simple application-level message protocol (read full article).
info Hint Building a microservice with Nest does not necessarily mean you must use the
@nestjs/microservicespackage. For example, if you want to communicate with external services (let's say other microservices written in different languages), you may not need all the features provided by
@nestjs/microservicelibrary. In fact, if you don't need decorators (
@MessagePattern) that let you declaratively define subscribers, running a Standalone Application and manually maintaining connection/subscribing to channels should be enough for most use-cases and will provide you with more flexibility.
With a custom transporter, you can integrate any messaging system/protocol (including Google Cloud Pub/Sub, Amazon Kinesis, and others) or extend the existing one, adding extra features on top (for example, QoS for MQTT).
info Hint To better understand how Nest microservices work and how you can extend the capabilities of existing transporters, we recommend reading the NestJS Microservices in Action and Advanced NestJS Microservices article series.
First, let's define a class representing our custom transporter.
warning Warning Please, note we won't be implementing a fully-featured Google Cloud Pub/Sub server in this chapter as this would require diving into transporter specific technical details.
In our example above, we declared the
GoogleCloudPubSubServer class and provided
close() methods enforced by the
Also, our class extends the
Server class imported from the
@nestjs/microservices package that provides a few useful methods, for example, methods used by Nest runtime to register message handlers. Alternatively, in case you want to extend the capabilities of an existing transport strategy, you could extend the corresponding server class, for example,
Conventionally, we added the
"Server" suffix to our class as it will be responsible for subscribing to messages/events (and responding to them, if necessary).
With this in place, we can now use our custom strategy instead of using a built-in transporter, as follows:
Basically, instead of passing the normal transporter options object with
options properties, we pass a single property,
strategy, whose value is an instance of our custom transporter class.
Back to our
GoogleCloudPubSubServer class, in a real-world application, we would be establishing a connection to our message broker/external service and registering subscribers/listening to specific channels in
listen() method (and then removing subscriptions & closing the connection in the
close() teardown method),
but since this requires a good understanding of how Nest microservices communicate with each other, we recommend reading this article series.
In this chapter instead, we'll focus on the capabilities the
Server class provides and how you can leverage them to build custom strategies.
For example, let's say that somewhere in our application, the following message handler is defined:
This message handler will be automatically registered by Nest runtime. With
Server class, you can see what message patterns have been registered and also, access and execute the actual methods that were assigned to them.
To test this out, let's add a simple
listen() method before
callback function is called:
After your application restarts, you'll see the following log in your terminal:
info Hint If we used the
@EventPatterndecorator, you would see the same output, but with the
isEventHandlerproperty set to
As you can see, the
messageHandlers property is a
Map collection of all message (and event) handlers, in which patterns are being used as keys.
Now, you can use a key (for example,
"echo") to receive a reference to the message handler:
Once we execute the
echoHandler passing an arbitrary string as an argument (
"Hello world!" here), we should see it in the console:
Which means that our method handler was properly executed.
As we mentioned in the first section, you don't necessarily need to use the
@nestjs/microservices package to create microservices, but if you decide to do so and you need to integrate a custom strategy, you will need to provide a "client" class too.
info Hint Again, implementing a fully-featured client class compatible with all
@nestjs/microservicesfeatures (e.g., streaming) requires a good understading of communication techniques used by the framework. To learn more, check out this article.
To communicate with an external service/emit & publish messages (or events) you can either use a library-specific SDK package, or implement a custom client class that extends the
ClientProxy, as follows:
warning Warning Please, note we won't be implementing a fully-featured Google Cloud Pub/Sub client in this chapter as this would require diving into transporter specific technical details.
As you can see,
ClientProxy class requires us to provide several methods for establishing & closing the connection and publishing messages (
publish) and events (
Note, if you don't need a request-response communication style support, you can leave the
publish() method empty. Likewise, if you don't need to support event-based communication, skip the
To observe what and when those methods are executed, let's add multiple
console.log calls, as follows:
With this in place, let's create an instance of
GoogleCloudPubSubClient class and run the
send() method (which you might have seen in earlier chapters), subscribing to the returned observable stream.
Now, you should see the following output in your terminal:
To test if our "teardown" method (which our
publish() method returns) is properly executed, let's apply a timeout operator to our stream, setting it to 2 seconds to make sure it throws earlier then our
setTimeout calls the
info Hint The
timeoutoperator is imported from the
timeout operator applied, your terminal output should look as follows:
To dispatch an event (instead of sending a message), use the
And that's what you should see in the console: