Playing Next Lesson In
seconds

Transcript

  1. So since we're accepting in user provided data here, what would be great is if we had some sort of validation on our query string to ensure we know exactly

  2. what's being provided in matches what we're expecting. If we take a look at the AdonisJS documentation within validators, if we scroll down a little bit, there's this validating cookies, headers and route params.

  3. I just wanna note that you can specifically validate them as such, so if you have specific cookies to validate, you can just add in those items to a cookies property,

  4. same with headers and same with your route parameters. Here we're working with query strings, but with Vine JS this approach is relatively straightforward as well. So let's hide our browser away, and first thing that we'll need to do

  5. is create a movie validator. Since we have the AdonisJS extension installed within Visual Studio Code, one thing we haven't covered yet is if we hold Command + Shift + P,

  6. and we start to type out AdonisJS, we could see ACE commands listed, so we can run them directly from our text editors command palette. Specifically what we wanna do is make a validator,

  7. so we can select that option. It's gonna ask us to provide a validator name. We'll go ahead and just call this our movie validator, and voila, it's gone ahead and created our validator for us,

  8. but it also opened that validator up automatically and set us up with Vine too. So what we wanna do is export const, and we'll call this our movie filter validator,

  9. equals vine.compile, and our ultimate goal here is to take the query string that we're getting from our request, which is of type record string any,

  10. and get a more safely defined type for that. And since that's a record string any, it's gonna be an object, so we can do vine.object, and start to define the object's properties. So what do we have?

  11. Well, we have a search key, which is our freeform text search, so we can do vine.specify that that should be a string value. It's most definitely an optional value as well, so we'll add that in.

  12. And then let's say maybe we wanted to limit this to just alphanumeric characters. We could do that there as well. And we also have our status, which is fine, and we have our status being provided

  13. into our query strings as the actual status's ID. So we could verify that that status exists as well, so we can normalize that to a type of number.

  14. Then we could do an exists check, and this works the exact same as you need. So we take in our database query builder, the value, and we don't need the field, and we'll just build out our query.

  15. So const exists equals await db.from our movie status's

  16. table, we will select just the ID where ID is our value, and then just grab the first result. Then we'll return and use the bang bang operator

  17. to convert that into a Boolean value. We also need to add a sync to this as well. Then additionally, we also want to make this optional too. Give that a save for formatting sake, and there we go.

  18. So now we'll require our status to be a valid type of number, and it will convert that one that we have currently as a string into a number for us as well.

  19. Then it will verify that it exists inside of our database within our movie status's table using the query that we've defined here. And of course, that value there is optional as a whole.

  20. Lastly, we have our sort. This will be a vine string. And just like we're doing with our status, we can verify that this exists as well. However, we don't need to use the database.

  21. Instead, we can just use our movie sort options within our movie service to verify that whatever's provided in matches one of our IDs there. So we can chain off of this exists.

  22. We don't need our database, so we'll just prefix that with an underscore, but we do need the value to find our callback function there. And we'll just return movie service,

  23. import that dot sort options dot some option where the option dot ID equals our value.

  24. And then we want to make this optional as well. So even though we require a sort, one may or may not be provided directly inside of our query strings. It's not until we reach our movie service

  25. and we actually execute and build out our query that we're finding and adding in that default sort if one's not provided. So we do want to make that optional there as well. And all right, we're seeing a red squiggly here

  26. because we're not returning back a promise of type Boolean. So we'll make that a promise by just wrapping that with async. Okay, so we now have our validator defined. Let's jump into our movies controller.

  27. And instead of directly grabbing our filters off of our query string as we are, we'll instead do const filters equals await.

  28. And we're going to want to import our movie filter validator dot and validate it using our request dot query string values. We don't want to do request dot validate using

  29. because we are specifically validating our requests query string. And we changed this name now from QS for query string to our filters there.

  30. We can also improve our methods parameter type here too. So we have it of type string any, but we can switch this to now infer directly from our validator what the return type will be.

  31. So we can use the infer helper from vine and import that from vine, vine JS types. And then we'll provide it our movie filter validator

  32. so that it can extract the types out of there. And we need that to be the type of, okay. So this doesn't look quite right. Yeah, our type is any, so let's scroll up here.

  33. And sure enough, our import did a relative import. You can either add dot JS to the end of that, or you can switch it to our hash if that does happen to you, okay. Now, if we scroll back down, this looks better.

  34. Let's check out the type of our filters. And now we have string or undefined for our search. Our status is number or undefined. And our sort is string or undefined. Those undefined are coming because we made all of them optional.

  35. And our status is being normalized to a type of number now, rather than a string. Furthermore, we have a red squiggly now on our status ID search,

  36. because we have number or undefined for the value, which is not assignable to a parameter of type chainable contract strict values. We do have this if check here,

  37. but this if check cannot inform TypeScript that we've done an actual if check, because we're applying the if conditional within a callback function. We, however, know that we have checked

  38. to verify that this does have a value. So we can tell TypeScript it's a okay, and does have a value by adding an exclamation point to the end of that property there. With that, we've now satisfied all of our typings

  39. and everything's back to being happy. We've also validated our query strings, and we're now only accepting in those validated query strings

  40. as our filters via our movie filter validator types or our get filter parameters. So if we jump back into our browser, give it a refresh to test everything out once more. Nope, that makes sense.

  41. We left it within our state. Just jump back into there and fix that real quick. So that's within our movies controller. Scroll back down. Sure enough, there it is. We just normalize that now to filters. Give that a save. Jump back into our browser once more.

  42. Give it a refresh. All right, everything's back to being happy, and we're still filtered by our one with our status ID of released. Let's test out our sort real quick, just to make sure that something's happening. And there we go.

  43. Okay, cool. Furthermore, if we were to switch our status to let's say we have all, we don't have this one within our database. So if we switch it to all, we search here, we get back our error.

  44. The selected status is invalid. So our validation is working there as well. But we do want to support all. So let's go ahead and add that into our validator options as well.

  45. So I'm not exactly sure what our all option is going to be coming up here with as a value. Let's go ahead and just console.log out what we're getting within our exists check.

  46. So console.log our value there. Give that a save. Let's quickly jump back into our browser, select our all, and run that real quick. Jump into our terminal real quick,

  47. and we see a value of zero as the first one, and then we got redirected back to our value of five. So it's coming through as a value of zero, which does not exist inside of our database.

  48. But we can easily make our exists happy with that. So we can just do a simple if not value return true. So if we don't get a value in any way, we'll just make that happy, bypass our exists,

  49. and say that it's valid because our status is perfectly fine not having a value in it. So with that applied, let's jump back into our browser, give this a test once more. So all there, search, and there we go.

  50. So now that is all back to being happy. Let's test things out once more just to make sure that everything without a query string is still valid as well. So we'll jump into just our movies page,

  51. resetting all of our filters, and it looks like everything worked there A-okay too.

Validating Query String Filter Values

@tomgobich
Published by
@tomgobich
In This Lesson

We'll learn how to use VineJS, AdonisJS' Validator, to validate the filter properties and values we have within our query string.

Join the Discussion 3 comments

Create a free account to join in on the discussion
  1. @adun

    I had a strange behavior, the movie index page search :

    • OK without a query filter

    • OK with an alphanumeric query filter

    • Fails after removing an existing query filter, showing the following error after the title input:

    The search field must contain only letters and numbers

    When the query filter is removed, the search field value becomes an empty string instead of undefined or null.

    To fix this behavior, add the following line after your import statements:

    vine.convertEmptyStringsToNull = true

    1
    1. Responding to adun
      @adun
      1
      1. Responding to adun
        @tomgobich

        Terribly sorry about that adun! I'm happy to see you were able to figure it out, and your assessment is spot-on! I'll circle back and insert a heads-up into this lesson. Thank you very much for sharing, and sorry again this got past me!

        1