00:00
(upbeat music)
00:02
So since we're accepting in user provided data here,
00:07
what would be great is if we had some sort of validation
00:09
on our query string to ensure we know exactly
00:11
what's being provided in matches what we're expecting.
00:14
If we take a look at the AdonisJS documentation
00:16
within validators, if we scroll down a little bit,
00:18
there's this validating cookies, headers and route params.
00:20
I just wanna note that you can specifically validate them
00:23
as such, so if you have specific cookies to validate,
00:26
you can just add in those items to a cookies property,
00:28
same with headers and same with your route parameters.
00:31
Here we're working with query strings,
00:33
but with Vine JS this approach is relatively
00:35
straightforward as well.
00:36
So let's hide our browser away,
00:37
and first thing that we'll need to do
00:38
is create a movie validator.
00:40
Since we have the AdonisJS extension installed
00:42
within Visual Studio Code, one thing we haven't covered yet
00:44
is if we hold Command + Shift + P,
00:46
and we start to type out AdonisJS,
00:48
we could see ACE commands listed,
00:50
so we can run them directly from
00:51
our text editors command palette.
00:53
Specifically what we wanna do is make a validator,
00:55
so we can select that option.
00:57
It's gonna ask us to provide a validator name.
00:59
We'll go ahead and just call this our movie validator,
01:01
and voila, it's gone ahead and created our validator for us,
01:04
but it also opened that validator up automatically
01:06
and set us up with Vine too.
01:07
So what we wanna do is export const,
01:09
and we'll call this our movie filter validator,
01:12
equals vine.compile, and our ultimate goal here
01:15
is to take the query string that we're getting
01:17
from our request, which is of type record string any,
01:21
and get a more safely defined type for that.
01:23
And since that's a record string any,
01:24
it's gonna be an object, so we can do vine.object,
01:27
and start to define the object's properties.
01:29
So what do we have?
01:30
Well, we have a search key,
01:31
which is our freeform text search,
01:32
so we can do vine.specify that that should be a string value.
01:36
It's most definitely an optional value as well,
01:38
so we'll add that in.
01:39
And then let's say maybe we wanted to limit this
01:41
to just alphanumeric characters.
01:42
We could do that there as well.
01:44
And we also have our status, which is fine,
01:47
and we have our status being provided
01:48
into our query strings as the actual status's ID.
01:52
So we could verify that that status exists
01:54
as well, so we can normalize that to a type of number.
01:57
Then we could do an exists check,
01:59
and this works the exact same as you need.
02:01
So we take in our database query builder, the value,
02:04
and we don't need the field,
02:05
and we'll just build out our query.
02:07
So const exists equals await db.from our movie status's
02:12
table, we will select just the ID where ID is our value,
02:17
and then just grab the first result.
02:19
Then we'll return and use the bang bang operator
02:21
to convert that into a Boolean value.
02:24
We also need to add a sync to this as well.
02:26
Then additionally, we also want to make this optional too.
02:29
Give that a save for formatting sake, and there we go.
02:31
So now we'll require our status to be a valid type
02:34
of number, and it will convert that one that we have
02:36
currently as a string into a number for us as well.
02:39
Then it will verify that it exists inside of our database
02:41
within our movie status's table using the query
02:44
that we've defined here.
02:45
And of course, that value there is optional as a whole.
02:47
Lastly, we have our sort.
02:49
This will be a vine string.
02:52
And just like we're doing with our status,
02:53
we can verify that this exists as well.
02:55
However, we don't need to use the database.
02:57
Instead, we can just use our movie sort options
02:59
within our movie service to verify that whatever's provided
03:02
in matches one of our IDs there.
03:04
So we can chain off of this exists.
03:06
We don't need our database, so we'll just prefix that
03:08
with an underscore, but we do need the value
03:10
to find our callback function there.
03:11
And we'll just return movie service,
03:14
import that dot sort options dot some option
03:18
where the option dot ID equals our value.
03:22
And then we want to make this optional as well.
03:24
So even though we require a sort,
03:26
one may or may not be provided directly
03:28
inside of our query strings.
03:29
It's not until we reach our movie service
03:31
and we actually execute and build out our query
03:33
that we're finding and adding in that default sort
03:35
if one's not provided.
03:36
So we do want to make that optional there as well.
03:38
And all right, we're seeing a red squiggly here
03:40
because we're not returning back a promise of type Boolean.
03:43
So we'll make that a promise
03:44
by just wrapping that with async.
03:45
Okay, so we now have our validator defined.
03:47
Let's jump into our movies controller.
03:49
And instead of directly grabbing our filters
03:52
off of our query string as we are,
03:54
we'll instead do const filters equals await.
03:57
And we're going to want to import our movie filter validator
04:00
dot and validate it using our request dot query string values.
04:05
We don't want to do request dot validate using
04:07
because we are specifically validating
04:09
our requests query string.
04:11
And we changed this name now from QS for query string
04:13
to our filters there.
04:15
We can also improve our methods parameter type here too.
04:18
So we have it of type string any,
04:20
but we can switch this to now infer directly
04:22
from our validator what the return type will be.
04:24
So we can use the infer helper from vine
04:27
and import that from vine, vine JS types.
04:29
And then we'll provide it our movie filter validator
04:33
so that it can extract the types out of there.
04:35
And we need that to be the type of, okay.
04:37
So this doesn't look quite right.
04:39
Yeah, our type is any, so let's scroll up here.
04:41
And sure enough, our import did a relative import.
04:43
You can either add dot JS to the end of that,
04:45
or you can switch it to our hash
04:47
if that does happen to you, okay.
04:48
Now, if we scroll back down, this looks better.
04:50
Let's check out the type of our filters.
04:52
And now we have string or undefined for our search.
04:54
Our status is number or undefined.
04:55
And our sort is string or undefined.
04:57
Those undefined are coming
04:58
because we made all of them optional.
05:00
And our status is being normalized
05:01
to a type of number now, rather than a string.
05:03
Furthermore, we have a red squiggly now
05:05
on our status ID search,
05:07
because we have number or undefined for the value,
05:10
which is not assignable to a parameter
05:12
of type chainable contract strict values.
05:14
We do have this if check here,
05:15
but this if check cannot inform TypeScript
05:18
that we've done an actual if check,
05:19
because we're applying the if conditional
05:21
within a callback function.
05:22
We, however, know that we have checked
05:24
to verify that this does have a value.
05:26
So we can tell TypeScript it's a okay,
05:27
and does have a value by adding an exclamation point
05:30
to the end of that property there.
05:31
With that, we've now satisfied all of our typings
05:33
and everything's back to being happy.
05:35
We've also validated our query strings,
05:37
and we're now only accepting in those validated query strings
05:40
as our filters via our movie filter validator types
05:43
or our get filter parameters.
05:45
So if we jump back into our browser,
05:47
give it a refresh to test everything out once more.
05:49
Nope, that makes sense.
05:50
We left it within our state.
05:51
Just jump back into there and fix that real quick.
05:52
So that's within our movies controller.
05:54
Scroll back down.
05:55
Sure enough, there it is.
05:55
We just normalize that now to filters.
05:57
Give that a save.
05:58
Jump back into our browser once more.
05:59
Give it a refresh.
06:00
All right, everything's back to being happy,
06:01
and we're still filtered by our one
06:03
with our status ID of released.
06:05
Let's test out our sort real quick,
06:06
just to make sure that something's happening.
06:08
And there we go.
06:09
Okay, cool.
06:10
Furthermore, if we were to switch our status
06:11
to let's say we have all,
06:12
we don't have this one within our database.
06:14
So if we switch it to all, we search here,
06:16
we get back our error.
06:17
The selected status is invalid.
06:19
So our validation is working there as well.
06:21
But we do want to support all.
06:23
So let's go ahead and add that
06:24
into our validator options as well.
06:26
So I'm not exactly sure what our all option
06:29
is going to be coming up here with as a value.
06:31
Let's go ahead and just console.log out
06:33
what we're getting within our exists check.
06:34
So console.log our value there.
06:36
Give that a save.
06:37
Let's quickly jump back into our browser,
06:39
select our all, and run that real quick.
06:41
Jump into our terminal real quick,
06:43
and we see a value of zero as the first one,
06:45
and then we got redirected back to our value of five.
06:48
So it's coming through as a value of zero,
06:49
which does not exist inside of our database.
06:51
But we can easily make our exists happy with that.
06:54
So we can just do a simple if not value return true.
06:57
So if we don't get a value in any way,
06:59
we'll just make that happy, bypass our exists,
07:01
and say that it's valid
07:02
because our status is perfectly fine
07:04
not having a value in it.
07:05
So with that applied, let's jump back into our browser,
07:07
give this a test once more.
07:08
So all there, search, and there we go.
07:10
So now that is all back to being happy.
07:12
Let's test things out once more
07:13
just to make sure that everything
07:14
without a query string is still valid as well.
07:17
So we'll jump into just our movies page,
07:19
resetting all of our filters,
07:20
and it looks like everything worked there A-okay too.
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!