WEBVTT

00:00.050 --> 00:07.190
So we want to add some validation to our newly created controller so that we validate the user's input,

00:07.190 --> 00:09.080
which is always best practice.

00:09.110 --> 00:18.470
So in order to add validation in our project, we're going to install class validator and class transformer.

00:18.920 --> 00:21.380
I also want to enhance our logging right now.

00:21.380 --> 00:28.910
So I found this really cool repository called Nestjs Piano, which is going to allow us to log every

00:28.910 --> 00:31.880
request and response in the system automatically.

00:31.880 --> 00:37.610
And what's really great about it is that it auto binds requests data to every log statement so that

00:37.610 --> 00:42.830
we can correlate our logs out of the box without any additional configuration.

00:42.830 --> 00:53.420
So we're going to go ahead and run NPM, install Nestjs piano and then piano HTTP as well, and then

00:53.420 --> 00:58.100
we'll go ahead and run npm start dev to start up our server again.

00:58.620 --> 01:03.480
Now let's go back to our app here and in our Reservations app.

01:03.480 --> 01:06.600
Let's open up the Main.ts folder.

01:06.810 --> 01:10.350
So to add validation to our app, it's very easy.

01:10.350 --> 01:15.720
All we have to do is call use global pipes here on our application.

01:15.720 --> 01:23.100
So validation is using class validator and class transformer, which we can run through as a nestjs

01:23.130 --> 01:28.580
pipe, which is essentially how nestjs abstracts transformation of request data.

01:28.590 --> 01:34.350
However, we can also validate request data and reject the request if it doesn't meet our validation.

01:34.350 --> 01:40.080
So we're going to call new validation pipe here and import this from Nestjs Common.

01:40.110 --> 01:47.400
Now to add that Pinot logger that we installed earlier as well, we're going to call App Dot use Logger

01:47.400 --> 01:55.290
and then we're going to call in app dot get, which is going to grab the logger here, which we're going

01:55.290 --> 01:58.950
to go ahead and actually import up top manually.

01:58.950 --> 02:07.950
We're going to import Logger here from Nestjs Pino And now importantly, we need to go to our reservations

02:07.950 --> 02:18.840
dot module and we will need to import the logger module from Nestjs Pino and call for root on that logger

02:18.840 --> 02:19.530
module.

02:20.130 --> 02:26.040
So after we go back to our terminal here, we can see that we have our new Pinot logger actually being

02:26.040 --> 02:28.020
used here and out of the box.

02:28.020 --> 02:32.490
It's actually pretty ugly looking and kind of hard to decipher a lot of these logs.

02:32.490 --> 02:39.000
So in order to fix this, we'll install another package here called Pinot Pretty, which will take care

02:39.000 --> 02:40.950
of formatting our logs.

02:40.950 --> 02:46.980
And now what we have to do is go back to our reservations module and in the logger module here in the

02:46.980 --> 02:53.310
for root call, we're going to pass in this Pinot HTTP options object.

02:53.340 --> 03:00.060
We'll add the transport object and then we'll have a target here, which we'll specify as Pinot pretty.

03:00.210 --> 03:06.690
So now if we go back to our terminal and start things back up, you can see we have a better looking

03:06.690 --> 03:07.050
logger.

03:07.050 --> 03:14.580
Now I'm also going to add an options object here and set single line to true so that our logs come out

03:14.580 --> 03:16.770
in a single line and there's no break here.

03:16.770 --> 03:18.480
So now let's open up Postman.

03:18.480 --> 03:25.570
And if we launch a get request at localhost 3000 slash reservations here, we can send a few requests

03:25.600 --> 03:26.170
off.

03:26.170 --> 03:33.370
And if we go back to our terminal now we have a request saying the system with a bunch of useful information,

03:33.370 --> 03:34.210
which is great.

03:34.210 --> 03:40.600
So you can see we have the request object here with an important ID attribute which for each request

03:40.600 --> 03:47.380
will be a unique value and that value will be persisted every time we use the logger during the request

03:47.380 --> 03:52.540
so that it's very easy to correlate logs with a given request.

03:52.540 --> 03:59.230
So we get a bunch of useful information as well as the URL here, any query parameters, headers, the

03:59.230 --> 04:05.380
host and various other properties as well as the response object itself here where we get the status

04:05.380 --> 04:08.710
code response headers and the response time.

04:08.710 --> 04:10.270
So this is very useful.

04:10.270 --> 04:16.570
And if we launch another request here for a non-existent reservation, I'll just use a bunch of numbers

04:16.570 --> 04:18.040
and send this request off.

04:18.040 --> 04:20.680
You can see we get an internal server error.

04:20.680 --> 04:26.560
And the reason for this is we can see in our errors that we're trying to cast the object ID, the value

04:26.560 --> 04:30.850
we pass in to an to an object ID which is not valid.

04:30.850 --> 04:36.220
But the important thing to get out of this is we get this really nice error message here which helps

04:36.220 --> 04:37.660
us understand what went wrong.

04:37.660 --> 04:45.040
But if you look down, you can see we have the request completed here with status code 500 and it has

04:45.040 --> 04:51.790
this request ID of four, which matches the same request ID for the error message here.

04:51.820 --> 04:52.900
ID four.

04:52.930 --> 04:53.260
Okay.

04:53.260 --> 04:59.350
So now that we actually have some good system logs that can log our request and responses, we're going

04:59.350 --> 05:04.690
to actually be using this same logging setup in the future for our other microservices.

05:04.690 --> 05:11.200
And also I don't like how we're exposing the implementation for our logger in our individual microservice.

05:11.200 --> 05:15.460
So if we have to reuse this, we're going to have to copy and paste all of this.

05:15.460 --> 05:21.730
I want to go ahead in our common library and create a new logger module that we can import into our

05:21.730 --> 05:23.470
individual microservices.

05:23.470 --> 05:29.740
So I'll run Nest, generate module, and then you can see here Nest will ask me which project I want

05:29.740 --> 05:30.610
to generate it to.

05:30.610 --> 05:33.100
In this case it'll be the common project.

05:33.100 --> 05:39.790
So then I'll restart my development server here, running NPM start dev, and now if we go back to our

05:39.790 --> 05:44.710
code here, we can see we have a new logger folder and the logger module.

05:44.710 --> 05:46.540
All we're going to do here is abstract.

05:46.540 --> 05:53.800
This logger module that we created and we are just going to paste it inside of this new logger module.

05:53.800 --> 05:58.360
Now we'll of course have to add the imports array and then we can paste it here.

05:58.360 --> 06:03.940
And then finally we'll copy the import from Pino and paste this up top.

06:04.240 --> 06:09.670
Now of course this import conflicts with our logger module, so we're going to import logger module

06:09.670 --> 06:12.700
as Pino Logger module.

06:12.710 --> 06:16.600
Then we'll go ahead and replace the logger module here with the Pino Logger module.

06:16.600 --> 06:23.800
And then lastly, we'll go ahead and create an index dot ts in our logger folder and export everything

06:23.800 --> 06:31.480
from the logger module, and then we'll go into the root export here and export everything from the

06:31.480 --> 06:32.800
logger folder.

06:32.800 --> 06:36.850
After we update our exports, we can then go into our reservations module.

06:36.850 --> 06:43.210
Now we're going to import the logger module from APP Common should be able to go back to our logs and

06:43.210 --> 06:46.000
see our mapping and pinot pretty.

06:46.000 --> 06:53.080
And if we send off that error request, we can still see it in the logs.

06:53.080 --> 07:00.330
So we want to continue adding validation to our reservations controller, particularly for our create

07:00.340 --> 07:01.720
reservation request.

07:01.720 --> 07:10.300
So we already added class validator in the bootstrap function by adding the validation pipe here as

07:10.300 --> 07:11.110
a global pipe.

07:11.110 --> 07:17.140
So now we'll go into our Create Reservation, DTO and we're going to actually use class validator as

07:17.140 --> 07:19.720
decorators here to validate this data.

07:19.960 --> 07:26.830
So we're going to do at is date and import is date here from class validator which will check if this

07:26.830 --> 07:28.180
value is a date.

07:28.210 --> 07:35.710
We'll do the same thing for end date here and then for place ID we can check that this is a valid string

07:35.710 --> 07:43.300
and that also it's not empty, which will make sure that this value actually exists and is not the empty

07:43.330 --> 07:43.900
string.

07:43.900 --> 07:48.280
And we can just copy this here and do the same for the invoice ID.

07:48.550 --> 07:54.670
So one more thing I want to add to our validation is I don't want the user to be able to pass additional

07:54.670 --> 08:00.940
properties that aren't in this DTO because for example, in a reservation service we're allowing any

08:00.940 --> 08:03.400
property to pass in on this DTO to be set.

08:03.400 --> 08:09.070
So I only want to allow properties that we define in this DTO in order to accomplish this with class

08:09.070 --> 08:15.430
validator, we can go back to our main.ts and provide an options object to our validation pipe property

08:15.430 --> 08:16.930
called whitelist.

08:16.930 --> 08:21.740
And we can see here if it's set to true, the validator will strip validated object of any properties

08:21.740 --> 08:23.090
that don't have decorators.

08:23.090 --> 08:25.580
So we'll set this value to true now.

08:25.580 --> 08:27.110
So this validation in place.

08:27.110 --> 08:33.800
Let's go back to Postman and we have our post request set up localhost 3000 slash reservations here

08:33.800 --> 08:38.000
and you can see this body we originally sent to the server which succeeded.

08:38.000 --> 08:44.780
If I send this off now, we get a 400 bad request response as well as this error payload here with a

08:44.780 --> 08:48.650
bunch of useful messages telling us what's wrong with our payload.

08:48.650 --> 08:52.220
So of course we need to add our missing properties here.

08:52.220 --> 08:57.260
So I've gone ahead and added the missing properties to our payload now and if we send this off, we

08:57.260 --> 09:02.180
still get this message saying that start date and end date are incorrect here because they must be a

09:02.180 --> 09:03.350
date instance.

09:03.350 --> 09:08.450
And you're probably wondering how this works because we can't obviously send a date instance to the

09:08.450 --> 09:09.980
server as JSON.

09:09.980 --> 09:16.970
So in order to remedy this, we can go back to our DTO here and so we can take advantage of class transformer

09:16.970 --> 09:21.020
to transform our incoming date strings into actual date objects here.

09:21.020 --> 09:24.230
Which class validator expects to do this?

09:24.230 --> 09:31.400
We can use this type decorator from class transformer up here, and if we look here, we can see that

09:31.400 --> 09:33.470
it specifies the type of the property.

09:33.500 --> 09:39.620
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

09:39.620 --> 09:42.020
thing here for the end date.

09:42.530 --> 09:48.590
So now if we go back to Postman and send this request off, we can see it finally succeeds and we get

09:48.590 --> 09:50.510
the created JSON here.

09:50.510 --> 09:55.850
And if we take the ID here of this reservation and launch a get request, we should be able to get that

09:55.850 --> 09:56.300
back.

09:56.300 --> 10:02.270
So just to show us that our whitelisting is working, we can try to provide an extra property here called

10:02.270 --> 10:03.080
Extra.

10:03.380 --> 10:11.000
Change this back to a post request and if we send this off, we can see we don't have this extra property

10:11.000 --> 10:12.320
actually persisted.

10:12.320 --> 10:16.400
So we can take the ID of this and launch a get request for it.

10:16.400 --> 10:19.760
And we should not expect to see that extra property, which we don't.

10:19.760 --> 10:22.040
So we can see our whitelisting working great.

10:22.040 --> 10:27.080
So now we have validation and system logging for request and responses in place.

10:27.080 --> 10:31.340
Let's go ahead and start adding some authentication to our back end in the next lecture.
