Coding

Mongoose Validation | Creating a REST API with Node.js

  • 00:00:02 welcome to another video in this
  • 00:00:04 building a restful service with no js'
  • 00:00:07 serious the last video we added Mongoose
  • 00:00:10 and we connected therefore our MongoDB
  • 00:00:12 database and we worked a lot on the
  • 00:00:14 products related routes in this video I
  • 00:00:17 want to find you an hour setup there a
  • 00:00:20 bit add validation so that only valid
  • 00:00:23 data makes it into our database and also
  • 00:00:27 come back to these restful constraints I
  • 00:00:29 showed you earlier this serious because
  • 00:00:32 right now our responses we give back
  • 00:00:36 from our endpoints here are not really
  • 00:00:39 dead great so let's work on both in this
  • 00:00:41 video
  • 00:00:45 let's start by adding validation now for
  • 00:00:49 that most interesting class is the post
  • 00:00:52 route here where we create new products
  • 00:00:56 and there we store an ID which we
  • 00:00:58 automatically generate so nothing to
  • 00:01:01 validate here this will be unique and so
  • 00:01:03 on but we also store a name and a price
  • 00:01:05 and what happens if a user passes let's
  • 00:01:09 say a string as a price well let's find
  • 00:01:12 out let's go back to postman create a
  • 00:01:14 new post request to products let's work
  • 00:01:17 on the body and the body now needs to
  • 00:01:19 change again should be a normal JSON
  • 00:01:22 object here ray set the name to
  • 00:01:25 something something awesome and then I
  • 00:01:32 set the price to hello which doesn't
  • 00:01:36 look like a correct price if I sent this
  • 00:01:39 and restart my server which I adjusted
  • 00:01:43 so if I sent this I do get back an error
  • 00:01:47 cast number failed for a value hello so
  • 00:01:50 that's great what important lecture we
  • 00:01:53 can already take away we can't store
  • 00:01:55 strings as numbers and so on this is
  • 00:01:57 already something mongoose checks for us
  • 00:01:59 and it correctly throws an error if we
  • 00:02:02 try to do so however what happens if we
  • 00:02:05 don't pass a price but just pass a name
  • 00:02:07 then we still get back a valid response
  • 00:02:10 and if I now get all my products you see
  • 00:02:14 we created a product without a price
  • 00:02:16 that is not how it should work so I'll
  • 00:02:20 go back to my product model and there
  • 00:02:24 where I set the values to string a
  • 00:02:25 number we can also pass an object which
  • 00:02:28 does not mean the type as an object but
  • 00:02:31 now we can provide a more detailed
  • 00:02:33 configuration we can set up a type
  • 00:02:36 property here where we can still set
  • 00:02:38 this to number so now we had the same as
  • 00:02:40 before but we can also pass more
  • 00:02:42 configuration like for example we could
  • 00:02:45 set required to true here and this does
  • 00:02:49 just what it sounds like it makes sure
  • 00:02:51 that this field is required if I now
  • 00:02:53 save this and I try to send that same
  • 00:02:56 post request from before again
  • 00:02:58 I now get an error that path price is
  • 00:03:01 required and this of course makes sense
  • 00:03:05 because well it is required I don't want
  • 00:03:09 to allow this right excess and the same
  • 00:03:13 can be done for the name of course here
  • 00:03:16 I'll now also set is to type string and
  • 00:03:18 require this and if I now send this here
  • 00:03:22 sorry without setting a name so let's
  • 00:03:25 just set some names property which
  • 00:03:27 doesn't exist now this also fails
  • 00:03:31 because price is required and name is
  • 00:03:34 required if I do set a name to something
  • 00:03:38 awesome and if I then do set a price to
  • 00:03:41 something awesome then it succeeds if I
  • 00:03:45 now add a third argument here like sales
  • 00:03:49 price which I set to 999 and I said this
  • 00:03:54 it also succeeds and if we get all
  • 00:03:57 products we see that here know where the
  • 00:04:02 sales price was safe though and that
  • 00:04:05 makes a lot of sense because if you have
  • 00:04:06 a look at our back end just sending the
  • 00:04:10 sales price as an argument to our route
  • 00:04:12 doesn't do anything because here we're
  • 00:04:15 configuring the product we plan on
  • 00:04:17 storing and there we never extract the
  • 00:04:19 sales price we never assign it to a
  • 00:04:21 property in our object which is going to
  • 00:04:24 get stored so we can send which every
  • 00:04:26 day that we want we're just making sure
  • 00:04:28 that the day that we do expect is valid
  • 00:04:31 and this is now the case with the set up
  • 00:04:33 we well set up here now let me quickly
  • 00:04:37 clean up that data here by sending a
  • 00:04:39 couple of delete requests so I'll delete
  • 00:04:42 this product which doesn't have a valid
  • 00:04:44 price and thereafter let me quickly
  • 00:04:48 again grab all products whoops
  • 00:04:51 without that ID which correctly failed
  • 00:04:54 because we just deleted it and let me
  • 00:04:56 also delete one of these something
  • 00:04:58 awesome products here or if you want of
  • 00:05:01 course both so let's delete that
  • 00:05:03 two now that also Rachelle's us some
  • 00:05:06 other issue i want to work on in this
  • 00:05:08 video the response now this is not a
  • 00:05:10 good response in at cert
  • 00:05:11 doesn't fulfill the constrains I
  • 00:05:14 mentioned earlier in this video series
  • 00:05:16 where the responses should be all kind
  • 00:05:18 of self-destructive so what we want to
  • 00:05:21 do is provide better responses let's
  • 00:05:23 start in products file here which is the
  • 00:05:27 only file where we really do something
  • 00:05:28 with the data and there let's take a
  • 00:05:32 closer look I do have my get request
  • 00:05:37 where I get all my Doc's and we can't
  • 00:05:40 improve that because if we send such a
  • 00:05:42 get request we clearly whoops without
  • 00:05:46 the idea again we clearly get back a
  • 00:05:50 response that is interesting to us it
  • 00:05:52 gives us all the IDS and we know that we
  • 00:05:55 can take the ID and appended at the end
  • 00:05:57 of the URL to get data for this object
  • 00:06:00 at but this is not necessarily something
  • 00:06:04 you know when you're a newcomer to this
  • 00:06:07 API so it would be nice if we could also
  • 00:06:09 provide a link here which you could then
  • 00:06:12 programmatically fetch to send another
  • 00:06:15 request to it and maybe we also want to
  • 00:06:18 send some metadata like the amount of
  • 00:06:20 items we fetched so overall I don't just
  • 00:06:23 want to return the docs here as the
  • 00:06:25 response I want to return more I'll get
  • 00:06:28 rid of the console log which I don't
  • 00:06:29 really need anymore
  • 00:06:30 and now I will create my response object
  • 00:06:35 here you can name this whatever you want
  • 00:06:37 and in this object I want if account for
  • 00:06:40 example and I will simply set is equal
  • 00:06:43 to Doc's length so that I give some meta
  • 00:06:46 information about the amount of elements
  • 00:06:48 we fetched thereafter I'll add a
  • 00:06:51 products property which now will be an
  • 00:06:54 array of all the products now there I'm
  • 00:06:58 not interested in this strange
  • 00:06:59 underscore underscore V thing which
  • 00:07:01 seems to be some internal thing so that
  • 00:07:04 is something I don't want to pass along
  • 00:07:06 I can make sure that I don't fetch it by
  • 00:07:09 adding some other method prior to exit
  • 00:07:12 the Select method where I can define
  • 00:07:15 which fields I want to select and you
  • 00:07:17 can pass a string here and there you can
  • 00:07:20 pass name price and underscore ID and
  • 00:07:24 this means I will
  • 00:07:25 fetch these fields and no other field
  • 00:07:27 and this is a great way of controlling
  • 00:07:29 which data you actually want to fetch so
  • 00:07:31 now if I attach Docs here it will be an
  • 00:07:34 array of talks without that strange
  • 00:07:36 underscore D thing and we can see this
  • 00:07:38 if we just save that and send again a
  • 00:07:40 get request to fetch all products now
  • 00:07:43 these are a new structure now we don't
  • 00:07:46 see the count field because I'm not
  • 00:07:48 using my new response constant here I
  • 00:07:50 should return that instead of the docs
  • 00:07:52 so now if I do that again you see now we
  • 00:07:55 got an object with count two products
  • 00:07:57 and then the products and then as I said
  • 00:08:00 would be interesting to also have more
  • 00:08:01 information on the individual product
  • 00:08:03 like the URL that leads us to the detail
  • 00:08:06 information for that product so for that
  • 00:08:09 I'll actually change my Doc's array here
  • 00:08:12 I can use the normal map method for that
  • 00:08:14 to basically map it into a new array
  • 00:08:17 there I'll get my individual document I
  • 00:08:20 fetched and I will return a new version
  • 00:08:23 of it now that new version will use all
  • 00:08:28 the old data and for that we could use
  • 00:08:32 the object spread operator if it is
  • 00:08:34 supported by the node.js version you're
  • 00:08:36 using but you can also map it manually
  • 00:08:38 to say name is doc name and price is
  • 00:08:42 dark price and then underscore ID is doc
  • 00:08:47 underscore ID but then also pass
  • 00:08:51 something like URL which could be a
  • 00:08:54 JavaScript object where we or maybe
  • 00:08:58 request which could be type where we say
  • 00:09:01 okay you can send a get request and this
  • 00:09:03 is just meta information you can write
  • 00:09:06 this in whichever way you want pass
  • 00:09:08 whichever information you want now I
  • 00:09:10 want to pass information about which
  • 00:09:12 kind of requests do you have to send to
  • 00:09:14 which URL to get more information about
  • 00:09:16 this object so here it would be a get
  • 00:09:19 request which you send to given URL that
  • 00:09:24 would be and now it's the URL of our
  • 00:09:27 service here so in my case is HTTP a
  • 00:09:30 localhost but of course you can also
  • 00:09:32 dynamically fetch this to get the ID off
  • 00:09:35 the service you are running on or you
  • 00:09:38 can of course
  • 00:09:38 also hard-code the domain in here which
  • 00:09:41 you're going to use in the end so I'm
  • 00:09:44 going to fetch my address here localhost
  • 00:09:48 3000 to be precise but then you would
  • 00:09:51 have to connect to products
  • 00:09:56 slash and then dark underscore ID
  • 00:10:01 whoops should be dot ID now if I save
  • 00:10:04 this and I send a get request we get
  • 00:10:08 back dead extra information with the URL
  • 00:10:11 I can send a get request you and I can
  • 00:10:14 indeed do this by simply clicking on it
  • 00:10:15 where I get detailed information for
  • 00:10:18 dead given object so this is now how we
  • 00:10:22 should style this and how we should
  • 00:10:24 structure this this is a better response
  • 00:10:26 for getting all the documents and of
  • 00:10:29 course you can find you just totally to
  • 00:10:31 your needs now let's have a look at the
  • 00:10:34 AMA routes if we created a new object
  • 00:10:37 right now I simply return a message
  • 00:10:41 which is okay but maybe not this message
  • 00:10:44 so we could say created object or
  • 00:10:47 created product successfully though that
  • 00:10:49 is kind of a redundant message because
  • 00:10:53 we do have status code 200 one but still
  • 00:10:55 let's keep it then create a product make
  • 00:10:58 sense but I don't just want to paste in
  • 00:11:01 the result here instead because let's
  • 00:11:05 maybe first have a look because if I do
  • 00:11:07 send such a post request here with the
  • 00:11:11 body I have here without the sales price
  • 00:11:14 maybe if I sent this what I get back as
  • 00:11:18 a response is also again with this
  • 00:11:20 strange V thing and so on so what I
  • 00:11:23 could do is I could also simply return a
  • 00:11:26 new object here where may be manually
  • 00:11:30 mapped name to result name price to
  • 00:11:33 result price and ID to result ID but
  • 00:11:39 where I then also and my request object
  • 00:11:42 I used before where I set the type equal
  • 00:11:45 to get and the URL to the same URL as
  • 00:11:48 above to gift user an idea about where
  • 00:11:51 to say
  • 00:11:52 a subsequent request to get detailed
  • 00:11:54 information about that new object the
  • 00:11:57 one difference is of course that here
  • 00:11:58 I'm not accessing doc ID but result ID
  • 00:12:01 so this would now be a better response
  • 00:12:03 for creating products if we have a look
  • 00:12:06 this looks good now we get this type of
  • 00:12:08 response and feel free to continue on
  • 00:12:11 your own of course to practice this we
  • 00:12:13 can do the same for the other routes too
  • 00:12:15 like here when we get data about one
  • 00:12:18 single object
  • 00:12:19 if I do send such a get request here we
  • 00:12:23 also get back that we thing the rest
  • 00:12:25 actually might be fine though you could
  • 00:12:26 also pass some meta information about
  • 00:12:28 where to send a request to get a list of
  • 00:12:30 all objects maybe so that is something
  • 00:12:33 you can change on that get route for a
  • 00:12:36 single object so on this route here for
  • 00:12:41 one I can add select here to say I only
  • 00:12:45 want to name the price and the ID and
  • 00:12:47 then as I just mentioned you can adjust
  • 00:12:50 the response here to not just give back
  • 00:12:53 the product which is your document but
  • 00:12:55 also that request metadata where you say
  • 00:12:58 yeah you could also send a get request
  • 00:13:02 to a URL and that URL which be HDPE
  • 00:13:07 localhost / products to get a list of
  • 00:13:10 all products and of course you could
  • 00:13:12 pass also some description property
  • 00:13:14 where you say get all products so that
  • 00:13:17 it's clearer what you're doing here or
  • 00:13:19 use some identifiers like get all
  • 00:13:22 products that could be more machine
  • 00:13:24 readable I'll remove that but you can
  • 00:13:27 style this and set this up in whichever
  • 00:13:29 way you need it where you want it for
  • 00:13:31 your API it is something you should
  • 00:13:33 always keep in mind though you create
  • 00:13:37 descriptive api's if you plan on using
  • 00:13:40 them publicly if it's only for you you
  • 00:13:42 might not need that but if you want to
  • 00:13:44 create an API that's used by other
  • 00:13:46 people you should be clear about how to
  • 00:13:48 use it and you do that by using it like
  • 00:13:50 this and for that let's also do the same
  • 00:13:54 for patching here we obviously also
  • 00:13:58 update and we then return the result of
  • 00:14:01 that operation which isn't that
  • 00:14:02 meaningful it's just is the strange set
  • 00:14:05 of now
  • 00:14:06 so there I could also just pass nothing
  • 00:14:08 that's also valid or simply pass
  • 00:14:12 something like an object where we have a
  • 00:14:14 message product updated and then again
  • 00:14:19 our request object to know that you can
  • 00:14:22 send a get request queue a URL to get
  • 00:14:25 the details about the new product and
  • 00:14:27 that would be localhost / products / and
  • 00:14:31 then the ID which we fetch from our URL
  • 00:14:35 here so that would be a meaningful
  • 00:14:38 response for patching so let's try this
  • 00:14:41 out – if I want to send a patch request
  • 00:14:43 to this URL and there I do instead
  • 00:14:46 indeed set a meaningful body well let's
  • 00:14:51 do that on the first tab where I already
  • 00:14:54 configured this a bit better so let's
  • 00:14:57 grab some ID here which I want to patch
  • 00:14:59 let's turn this to a patch request and
  • 00:15:02 then remember here the body is array
  • 00:15:04 where we got a couple of objects
  • 00:15:06 describing the operations we want to do
  • 00:15:08 like here we would set the prop name
  • 00:15:12 property – let's say name we want to
  • 00:15:15 change the name of a product and then
  • 00:15:17 the value will be something more useful
  • 00:15:23 if I now send this I get back this
  • 00:15:27 updated response and I can send a get
  • 00:15:29 request to dis of course and fail oh
  • 00:15:34 because I of course should encode the
  • 00:15:37 port into that URL queue so that is
  • 00:15:41 something I also need to do up here
  • 00:15:42 where I return the data for a single
  • 00:15:45 Product ID so this should be the full
  • 00:15:47 domain of course so with that fixed
  • 00:15:50 let's try this again let's again patch
  • 00:15:53 this object here and then simply send a
  • 00:15:57 get request and now we get that data for
  • 00:15:59 this changed object and with that for
  • 00:16:04 deleting we also might do something
  • 00:16:06 comparable here we try to delete this
  • 00:16:10 object and if we succeed I can either
  • 00:16:13 send an empty object or again something
  • 00:16:15 with a message where I say product
  • 00:16:17 deleted and here
  • 00:16:19 I don't need to send in a link to the
  • 00:16:21 product because it was deleted but we
  • 00:16:23 could add our request object here maybe
  • 00:16:26 to show hey you could also send a post
  • 00:16:28 request to some other URL so here would
  • 00:16:32 be HTTP localhost 3,000 products to
  • 00:16:36 create a new product there you will need
  • 00:16:39 to send data and that data then would
  • 00:16:44 simply be in the form of having a name
  • 00:16:48 which is a string and price which is a
  • 00:16:54 number and with that we would give
  • 00:16:56 instructions what the request body
  • 00:16:58 should look like maybe we name this body
  • 00:17:00 therefore so now if we save this and we
  • 00:17:03 delete our object here we get back this
  • 00:17:07 language cells hey you can send a post
  • 00:17:09 request to this URL and if you pass that
  • 00:17:11 payload then you will create a new
  • 00:17:13 product so these are more meaningful
  • 00:17:16 responses and you can as I said
  • 00:17:18 fine-tune them to your needs you might
  • 00:17:20 need different responses obviously I
  • 00:17:22 think we spent enough time on that
  • 00:17:24 now one thing because just as I said I
  • 00:17:26 want to work on it's just application
  • 00:17:28 warning which you also might have seen
  • 00:17:31 and which you may be also asked about
  • 00:17:32 already now this is not a problem you
  • 00:17:35 could ignore it but we also can fix it
  • 00:17:38 by going back to the app.js file and
  • 00:17:41 there when we set up mangu is right when
  • 00:17:43 we connect it there I can also say
  • 00:17:47 Mongoose promise equals global promise
  • 00:17:52 to use the default nodejs promise
  • 00:17:54 implementation instead of the Mongoose
  • 00:17:56 one and with that if you restart the
  • 00:17:58 server you shouldn't get this anymore so
  • 00:18:00 now if you again start sending your
  • 00:18:03 requests and so on it should still work
  • 00:18:06 as before but you shouldn't get this
  • 00:18:08 deprecation warning now feel free to
  • 00:18:10 find you this more to adjust it to your
  • 00:18:13 needs and requirements I'm happy with
  • 00:18:15 the current state and I'd say in the
  • 00:18:17 next video we take a closer look at
  • 00:18:19 further evolving our data structures and
  • 00:18:22 also adding some orders and making sure
  • 00:18:27 that we save these in the database too