So we want to add some validation to our newly created controller so that we validate the user's input,
which is always best practice.
So in order to add validation in our project, we're going to install class validator and class transformer.
I also want to enhance our logging right now.
So I found this really cool repository called Nestjs Piano, which is going to allow us to log every
request and response in the system automatically.
And what's really great about it is that it auto binds requests data to every log statement so that
we can correlate our logs out of the box without any additional configuration.
So we're going to go ahead and run NPM, install Nestjs piano and then piano HTTP as well, and then
we'll go ahead and run npm start dev to start up our server again.
Now let's go back to our app here and in our Reservations app.
Let's open up the Main.ts folder.
So to add validation to our app, it's very easy.
All we have to do is call use global pipes here on our application.
So validation is using class validator and class transformer, which we can run through as a nestjs
pipe, which is essentially how nestjs abstracts transformation of request data.
However, we can also validate request data and reject the request if it doesn't meet our validation.
So we're going to call new validation pipe here and import this from Nestjs Common.
Now to add that Pinot logger that we installed earlier as well, we're going to call App Dot use Logger
and then we're going to call in app dot get, which is going to grab the logger here, which we're going
to go ahead and actually import up top manually.
We're going to import Logger here from Nestjs Pino And now importantly, we need to go to our reservations
dot module and we will need to import the logger module from Nestjs Pino and call for root on that logger
module.
So after we go back to our terminal here, we can see that we have our new Pinot logger actually being
used here and out of the box.
It's actually pretty ugly looking and kind of hard to decipher a lot of these logs.
So in order to fix this, we'll install another package here called Pinot Pretty, which will take care
of formatting our logs.
And now what we have to do is go back to our reservations module and in the logger module here in the
for root call, we're going to pass in this Pinot HTTP options object.
We'll add the transport object and then we'll have a target here, which we'll specify as Pinot pretty.
So now if we go back to our terminal and start things back up, you can see we have a better looking
logger.
Now I'm also going to add an options object here and set single line to true so that our logs come out
in a single line and there's no break here.
So now let's open up Postman.
And if we launch a get request at localhost 3000 slash reservations here, we can send a few requests
off.
And if we go back to our terminal now we have a request saying the system with a bunch of useful information,
which is great.
So you can see we have the request object here with an important ID attribute which for each request
will be a unique value and that value will be persisted every time we use the logger during the request
so that it's very easy to correlate logs with a given request.
So we get a bunch of useful information as well as the URL here, any query parameters, headers, the
host and various other properties as well as the response object itself here where we get the status
code response headers and the response time.
So this is very useful.
And if we launch another request here for a non-existent reservation, I'll just use a bunch of numbers
and send this request off.
You can see we get an internal server error.
And the reason for this is we can see in our errors that we're trying to cast the object ID, the value
we pass in to an to an object ID which is not valid.
But the important thing to get out of this is we get this really nice error message here which helps
us understand what went wrong.
But if you look down, you can see we have the request completed here with status code 500 and it has
this request ID of four, which matches the same request ID for the error message here.
ID four.
Okay.
So now that we actually have some good system logs that can log our request and responses, we're going
to actually be using this same logging setup in the future for our other microservices.
And also I don't like how we're exposing the implementation for our logger in our individual microservice.
So if we have to reuse this, we're going to have to copy and paste all of this.
I want to go ahead in our common library and create a new logger module that we can import into our
individual microservices.
So I'll run Nest, generate module, and then you can see here Nest will ask me which project I want
to generate it to.
In this case it'll be the common project.
So then I'll restart my development server here, running NPM start dev, and now if we go back to our
code here, we can see we have a new logger folder and the logger module.
All we're going to do here is abstract.
This logger module that we created and we are just going to paste it inside of this new logger module.
Now we'll of course have to add the imports array and then we can paste it here.
And then finally we'll copy the import from Pino and paste this up top.
Now of course this import conflicts with our logger module, so we're going to import logger module
as Pino Logger module.
Then we'll go ahead and replace the logger module here with the Pino Logger module.
And then lastly, we'll go ahead and create an index dot ts in our logger folder and export everything
from the logger module, and then we'll go into the root export here and export everything from the
logger folder.
After we update our exports, we can then go into our reservations module.
Now we're going to import the logger module from APP Common should be able to go back to our logs and
see our mapping and pinot pretty.
And if we send off that error request, we can still see it in the logs.
So we want to continue adding validation to our reservations controller, particularly for our create
reservation request.
So we already added class validator in the bootstrap function by adding the validation pipe here as
a global pipe.
So now we'll go into our Create Reservation, DTO and we're going to actually use class validator as
decorators here to validate this data.
So we're going to do at is date and import is date here from class validator which will check if this
value is a date.
We'll do the same thing for end date here and then for place ID we can check that this is a valid string
and that also it's not empty, which will make sure that this value actually exists and is not the empty
string.
And we can just copy this here and do the same for the invoice ID.
So one more thing I want to add to our validation is I don't want the user to be able to pass additional
properties that aren't in this DTO because for example, in a reservation service we're allowing any
property to pass in on this DTO to be set.
So I only want to allow properties that we define in this DTO in order to accomplish this with class
validator, we can go back to our main.ts and provide an options object to our validation pipe property
called whitelist.
And we can see here if it's set to true, the validator will strip validated object of any properties
that don't have decorators.
So we'll set this value to true now.
So this validation in place.
Let's go back to Postman and we have our post request set up localhost 3000 slash reservations here
and you can see this body we originally sent to the server which succeeded.
If I send this off now, we get a 400 bad request response as well as this error payload here with a
bunch of useful messages telling us what's wrong with our payload.
So of course we need to add our missing properties here.
So I've gone ahead and added the missing properties to our payload now and if we send this off, we
still get this message saying that start date and end date are incorrect here because they must be a
date instance.
And you're probably wondering how this works because we can't obviously send a date instance to the
server as JSON.
So in order to remedy this, we can go back to our DTO here and so we can take advantage of class transformer
to transform our incoming date strings into actual date objects here.
Which class validator expects to do this?
We can use this type decorator from class transformer up here, and if we look here, we can see that
it specifies the type of the property.
So all we want to do here is specify that we want this to be returned as a date and we'll do the same
thing here for the end date.
So now if we go back to Postman and send this request off, we can see it finally succeeds and we get
the created JSON here.
And if we take the ID here of this reservation and launch a get request, we should be able to get that
back.
So just to show us that our whitelisting is working, we can try to provide an extra property here called
Extra.
Change this back to a post request and if we send this off, we can see we don't have this extra property
actually persisted.
So we can take the ID of this and launch a get request for it.
And we should not expect to see that extra property, which we don't.
So we can see our whitelisting working great.
So now we have validation and system logging for request and responses in place.
Let's go ahead and start adding some authentication to our back end in the next lecture.