All right.
So to get started integrating gRPC with our microservices, we're going to need to install some dependencies
that Nestjs will use underneath the hood.
To use gRPC.
Now, as I've done, typically I'm going to keep the completed code for this gRPC work in a new branch
called gRPC.
So if you want to see the completed project with gRPC, you can check out this branch and follow along
there.
I'm going to check out the main branch and work off of this on a clean slate.
So let's go ahead and get started by installing those dependencies.
So I will run npm install, dash dash, save grab gRPC, slash gRPC, JS and gRPC slash proto loader.
We're also going to install TS, proto TS Dash Proto, which is going to allow us to convert our Protobuf
definition files, which is the shape of our data and services in a language that gRPC understands.
It's going to allow us to convert those files into TypeScript code so that we don't have to keep duplicating
all of that.
And it's also going to allow us to integrate this in Nestjs very easily.
So let's go ahead and let these dependencies finish installing.
So now that we have our dependencies installed, let's go back to our project and we're going to create
a new folder at the root of our project called Proto.
And this is where the definitions, those protobuf definitions that I mentioned earlier, this is where
we're going to host all of them.
And this is going to be the data and services that act on those data.
It's going to be a description of that in a language that gRPC understands.
And the big advantage of using these files is that we can use them on any client.
So whether it's our back end here, a mobile app, a web page, any application that wants to use these
products can be shared.
And we don't have to keep duplicating all of these types, which is a big benefit of using gRPC.
So let's start off with defining the Proto for our auth service.
I'm going to create an auth dot proto first.
And to start off we need to define the syntax that we're using.
So there's different Syntaxes for Proto, we can use proto two or proto three, we're going to use proto
three because that's the most up to date version currently.
And we specify this by running syntax equals quotes proto three and have a colon.
Now if you notice I have syntax highlighting enabled on my VS code.
If you'd like this as well, you can go to the extensions page and make sure you have the proto vs code
proto three extension installed, which is what I'm using, which is going to allow us to have proto
three support for VS code and give us IntelliSense highlighting directly, which is very useful.
So next we're going to set the name of the current package we are using.
So in this case we'll call it auth since we're defining the auth proto and then we define the service
that we are going to be describing.
So in this case we're going to be describing the auth service with all of the methods that are exposed
on the auth service.
Now you can think of this as the controller currently in our application.
So if we go to the auth controller we have now that's using micro services, we have our one method
here to authenticate over our micro services.
So we want to describe that method here in the auth service definition by writing RPC to describe a
remote procedural call.
And then we can call it whatever we want, we're going to stick with the name authenticate and this
is going to look very similar to a function definition.
So we'll open up parentheses here where we can describe the parameters that this RPC method will take.
In this case, we'll create a new type called authentication, and then we'll specify that this is going
to return a user message and open up curly brackets.
So this is our method that we've defined here and now we need to describe these types that we're using.
So, so we do this by using the message keyword and then we will create an authentication message.
And now we can describe all of the properties that exist on this message.
So in our case, when we have a request that comes in to this authenticate route, we know what's going
to be sent along with it is our JWT token, right?
So we have currently our auth guard that's protecting this route.
We know that the JWT auth guard is using the JWT strategy, which is going to be pulling the authentication
or JWT off of the incoming request.
So let's go ahead and describe this with an authentication property and then we're going to set this
equal to one because all of our properties in our messages need to be ordered.
So we'll describe this with the one number as the first property.
Next we will describe our return type here, which is just going to be the user.
So we have the user message and we will have a string that represents the ID of the user.
Now we know in our application.
The ID is going to be underscore ID.
However, an RPC doesn't support the use of underscores, so we're going to have to use just an ID property
here and then map this accordingly in our application.
So let's go ahead and specify this as the first property and then we'll fill out the rest of our properties
on a user.
We know that we have the email, the password and of course we can reference the user schema if we need
to be reminded of all the properties on the user.
Of course we have the roles as well.
So to describe an array in gRPC, we can use the repeated keyword which says that this property will
be repeated in an array and we'll set this equal to four.
So it's a repeated string where we have our roles.
So this is all we have to do for our auth Proto, we have all of the methods for our auth service defined
and all of the types that we're going to be using.
So next we can move on to our other Proto.
So let's move on to the payments proto, which will represent our payments micro service.
So we'll start off by defining the syntax which will be again proto three and then our package will
be payments.
In this case because we're working with the payments proto and then we'll define the payments service
so we can remind ourselves by going to the payments controller to see our one method here, which is
create charge.
So we'll need to define this in our payments proto, so we'll have an RPC called Create Charge that
will take in a create charge message that we will define and this will return a create charge response
and we'll have a curly bracket.
So let's go ahead and define the crate charge message firstly, which is going to be the same thing
as our current payment create charge DTO.
So we'll copy over the email from this DTO, and then if we go back here, we see we're of course extending
the crate charge DTO so if we look at this, we have the card and then the amount property.
So let's go ahead and firstly define the amount which is going to be an INT 32, which represents an
integer and we'll set this equal to two.
And then I want to define a new type in here called card message.
Set it equal to card and set it equal to three so we can reference other types within our messages as
we're doing here.
And then we'll define this card message which holds all of the properties associated with the card.
So now if we go to the Create Charge, DTO, we can see the card DTO where we have all of these properties.
So let's go ahead and fill these out in our card message.
So we have the string CVC, we have the INT 32.
Expiration month.
Now, as we did with the ID earlier, we can't use underscores.
So we're going to use camel case and then map this at the application layer later on.
So next we have the expiration year, which we will also camel case.
And then finally we have the number which will be the actual credit card number.
It will set equal to four.
Now we can see our compiler's complaining because we haven't defined the create charge response.
So the create charge response is going to be whatever is returned from the Stripe API.
So right now we know we're returning the payment intent from the Stripe API.
And the payment intent is a really large object that contains a lot of properties and we don't really
care about most of these properties.
Really, all that we're concerned about is the ID that's returned from the payment intent and we can
confirm this by going to the reservation service where we actually call the payment service, right?
We get back the response from the payment service and we're just pulling the ID off of the payment intent,
which we can map back to the invoice ID on the reservation.
And we don't care about all the other properties on the payment intent.
So in this create charge response, instead of having to define all of the properties on the payment
intent, I'm just going to create this create charge response and I'm only going to list the ID property
here, which is a string because that's the only property we care about and want to actually access
on this object.
Now we can see our payments.
Proto is fully completed.
We can move on to the last proto that we need to define and this will be the notifications proto.
So we'll follow the same pattern as we've done before.
We'll set the syntax equal to proto three and then we will specify the package is notifications, and
then we'll define the notifications service and we can reference the notifications controller to remind
herself that we have our notify email method that we need to define.
So let's go ahead and define the notify email rpc method that will take in a notify email message and
return.
Well, it's actually not going to return anything.
However, we still need to we still need to define some sort of message in this return statement.
So we'll use an empty return type that we will define.
So let's go ahead and define the empty message here called empty.
That is just going to have no properties on it.
And then if we go back to the notifications controller in our notify email, DTO, we see we have the
two properties that we need to define the email and the text, which are both strings.
So we'll go ahead and create a new message for notify email message, and we will define the string
of email and the string text.
So now we have completed each of these proto definitions that describe each of our microservices.
It describes all of the data and methods on these services, so the clients and servers will both know
exactly which methods can be called and what data will be required and returned from those methods.
Next up, let's go ahead and utilize Proto to actually generate TypeScript types from these definitions
which we can utilize in our application.