- 00:00:02 welcome to another video in this series
- 00:00:04 where we build a restful api with nodejs
- 00:00:07 the previous videos we had a look at how
- 00:00:10 we work on our products and how we store
- 00:00:12 products in the database and that's all
- 00:00:15 working now when I work on the orders
- 00:00:17 and the interesting part here of course
- 00:00:19 is that orders are connected to products
- 00:00:22 so let's dive into that in this video
- 00:00:28 so let's start working on that and the
- 00:00:30 first thing I want to do is I want to
- 00:00:32 create a model for my orders so I
- 00:00:35 ordered J's file now copy the logic from
- 00:00:39 the product JS file to import non-use
- 00:00:41 and export a model at the end though the
- 00:00:44 schema will be named order schema and
- 00:00:47 I'll name the model order – now how
- 00:00:52 should that look like do I want to have
- 00:00:55 an ID and whatever unique ID per order I
- 00:00:58 also need one important piece of
- 00:01:01 information I need to know which product
- 00:01:04 was ordered
- 00:01:05 now you could of course create a way
- 00:01:08 more complex setup where you have carts
- 00:01:10 so shopping carts which comprise a
- 00:01:13 couple of our products and quantities I
- 00:01:17 want to start and stick simple here I
- 00:01:20 want to say okay I got an order and that
- 00:01:22 order is connected to one product which
- 00:01:25 I ordered so how does this work then
- 00:01:27 I'll add a product key and I'll set this
- 00:01:32 equal to an object now the type here
- 00:01:36 will actually also be an object ID
- 00:01:40 because I will store the ID of the
- 00:01:43 product which is related to this order
- 00:01:46 now there's something important we're
- 00:01:48 now creating like a relation and MongoDB
- 00:01:52 is a non relational database you can
- 00:01:55 kind of rebuild these relations as we're
- 00:01:57 about to do that and that is okay to
- 00:02:00 some degree but if you find yourself
- 00:02:03 building a lot of relations and sticking
- 00:02:06 very much in that sequel database world
- 00:02:10 then you might indeed wanna pick a
- 00:02:12 sequel database because you might not
- 00:02:14 need the advantages of a no sequel
- 00:02:16 database like MongoDB but you will just
- 00:02:19 get all the disadvantages if you work
- 00:02:22 with a lot of relations now this one
- 00:02:24 relation here that's perfectly fine but
- 00:02:27 I just want to bring this to your
- 00:02:28 attention so let's set up the relation
- 00:02:31 here then and as I said first of all
- 00:02:33 let's assign you type to be an object ID
- 00:02:36 and then there's an important keyword
- 00:02:38 ref you need to use ref here to
- 00:02:40 configure this
- 00:02:41 type and ref should now hold a string
- 00:02:45 with the name of the model you want to
- 00:02:47 connect this model to know if we have a
- 00:02:50 look at our product JS file we see that
- 00:02:52 the name is product so it's this name we
- 00:02:55 assigned here and that's the name I now
- 00:02:57 always use here I want to connect this
- 00:02:59 schema with my product and with that I
- 00:03:04 store my order where I say we ordered
- 00:03:08 this product now let's say a product or
- 00:03:12 excuse me an order also knows a quantity
- 00:03:14 here we could set a type to be number
- 00:03:18 and we can set required to true because
- 00:03:21 I want to have a quantity on each order
- 00:03:23 you could of course also make this
- 00:03:24 optional and you could also say okay I
- 00:03:27 might not require it I'll just set a
- 00:03:30 default which is one that's also an
- 00:03:34 option now just required and default
- 00:03:36 together don't make a lot of sense but
- 00:03:38 in this case you're saying you don't
- 00:03:40 need to pass me a quantity if you pass
- 00:03:43 me nothing I'll use that default of one
- 00:03:45 so we'll always store a quantity in the
- 00:03:48 database it's just going to be our
- 00:03:50 default quantity if no data there are no
- 00:03:53 additional information is passed and
- 00:03:54 that is a very simple order look at the
- 00:03:56 product we got the quantity the price as
- 00:03:59 part of the product so we can calculate
- 00:04:01 the total price and with that we got a
- 00:04:04 schema setup for orders now let's use
- 00:04:06 that scheme and our order J's file
- 00:04:09 before we fetch all orders let's work on
- 00:04:12 hosting them now for that I'll import
- 00:04:15 two things I'll import Mongoose to
- 00:04:17 create an ID so I'll simply require
- 00:04:20 Mongoose here and I will of course also
- 00:04:24 import my order model from the models
- 00:04:30 file and there from the order J's file
- 00:04:32 with there we can create a new order so
- 00:04:36 I'll create an order but I'll treat it
- 00:04:38 differently with new order so using our
- 00:04:41 model as a constructor which we can of
- 00:04:43 course and then passing a JavaScript
- 00:04:45 object where we will configure it now
- 00:04:47 I'll set the ID equal to Mongoose types
- 00:04:52 object ID executed as a function to
- 00:04:55 automatically generate an idea then I
- 00:04:58 can set the quantity to request body
- 00:05:03 quantity though that might not be sad
- 00:05:07 sodas might be null but it should still
- 00:05:09 work we'll see if it does and I also
- 00:05:12 need to set my product and now this
- 00:05:14 needs to be the idea of the product
- 00:05:16 we're connected to and I expect to get
- 00:05:18 this a request body product ID maybe
- 00:05:22 this also means that back in the order
- 00:05:25 model we probably want to set this to be
- 00:05:29 required so that we can't pass null here
- 00:05:34 so back here in our order J's file the
- 00:05:37 routes file we're now creating our order
- 00:05:41 or storing it in the database where
- 00:05:43 we're trying we're setting it up we
- 00:05:45 cannot store it by a chaining save and
- 00:05:47 then on save we can chain then or
- 00:05:54 actually we can't chain exit to turn it
- 00:05:56 into a real promise then and catch are
- 00:05:58 also provided on the return values by
- 00:06:01 default but that's they're not a real
- 00:06:03 problems with the ex ecute is so now we
- 00:06:05 can add them and catch and then we can
- 00:06:10 use the result we're getting back here
- 00:06:13 you may be log it to the console and of
- 00:06:17 course to also resend a status code of
- 00:06:19 201 where we essentially for now send a
- 00:06:22 simple results but response we're going
- 00:06:24 to find you this where a just return the
- 00:06:27 result and when we catch an error I'm
- 00:06:31 going to console lock that error here
- 00:06:34 and I'm going to return a response with
- 00:06:36 status code 500 where I send an error or
- 00:06:40 property that holds the error we got and
- 00:06:43 I'll remove the default damage response
- 00:06:45 we used previously let's find out if
- 00:06:48 that works now for that let's go back
- 00:06:51 and let's first of all get the list of
- 00:06:53 products and restart our server first of
- 00:06:58 all so let's get a list of products
- 00:07:00 maybe this one with that ID let's copy
- 00:07:03 the ID let's create a new post request
- 00:07:06 which targets slash orders to send a
- 00:07:08 post
- 00:07:09 to that endpoint not send a JSON body
- 00:07:13 which should be a normal JSON object
- 00:07:16 where I do set you know this is
- 00:07:19 important where I do set quantity and
- 00:07:22 product ID so I'll add product ID as a
- 00:07:28 key here and past the idea just copied
- 00:07:31 and I'll set quantity as a key here and
- 00:07:35 set this to two now if I had sent or I
- 00:07:40 get an error accident work
- 00:07:44 yeah true we're saving here so generally
- 00:07:47 that was true
- 00:07:47 exit gives you a real promise whereas
- 00:07:50 normal queries and save is not a curious
- 00:07:53 this but queries we're used find for
- 00:07:56 example these give you back a Venn
- 00:07:59 method but no catch method and then you
- 00:08:01 can use X Act to turn it into a real
- 00:08:03 promise for save you actually get a real
- 00:08:05 promise by default so that out of the
- 00:08:08 way let's try that again now here we see
- 00:08:11 we created a new object with its own ID
- 00:08:15 which is different to the Product ID of
- 00:08:17 course we store it the product ID and we
- 00:08:19 got the quantity of two now let's all
- 00:08:22 try not passing a quantity if I now hit
- 00:08:25 Send we see we also get this default
- 00:08:27 quantity of 1 and if I now don't send an
- 00:08:31 ID so I don't send anything we get an
- 00:08:34 error that the product is required which
- 00:08:36 makes sense so now we can successfully
- 00:08:39 create new orders let's make sure we can
- 00:08:41 also view all orders by going to our
- 00:08:44 general get route here and let's use
- 00:08:46 order here together with fine with no
- 00:08:49 arguments to find all orders if you pass
- 00:08:52 an argument to find it would be an
- 00:08:53 object that describes the object you
- 00:08:55 want to find just as we use the for
- 00:08:57 update and delete but here I want to
- 00:08:59 find all orders and I simply will
- 00:09:02 execute X X to again turn it into a real
- 00:09:05 promise here and then chain then and
- 00:09:07 catch and then let's see what we can do
- 00:09:10 here let's use all the docs we get back
- 00:09:12 and for now simply return them with a
- 00:09:14 status code of 200 Jason Docs like that
- 00:09:18 and for an error I will send a status
- 00:09:22 code of
- 00:09:23 500 where I said Jason to an object were
- 00:09:27 we store and return the error now if
- 00:09:32 dad's setup let's save this and let's
- 00:09:34 try it out by sending a get request to
- 00:09:38 orders like this for hit Send here I get
- 00:09:42 all the orders I did create so the two
- 00:09:45 with quantity two I stored and the one
- 00:09:48 with quantity one so this is now my get
- 00:09:54 request
- 00:09:55 now let's already work on the response
- 00:09:57 layout so here for example I don't want
- 00:10:00 to return that the part and I want to
- 00:10:03 return more information than just the
- 00:10:05 array of items so I'll add select prior
- 00:10:08 to exit and just select the product and
- 00:10:12 the quantity field and the ID let's say
- 00:10:16 and then again here I'll return an
- 00:10:19 object where I have count which is Doc's
- 00:10:22 length and where I then also have my
- 00:10:26 orders which is now docks and where I
- 00:10:29 have my request object let's say with a
- 00:10:32 type of get because I also want to
- 00:10:37 support details on our individual order
- 00:10:39 so get to a URL which would be HTTP
- 00:10:43 localhost 3,000 orders slash and then it
- 00:10:48 would be our ID of that document now to
- 00:10:53 get this back I made a mistake
- 00:10:57 of course shouldn't return orders like
- 00:10:59 this I should map it here to get the
- 00:11:02 individual doc and then return a new
- 00:11:04 object here for that document we got
- 00:11:07 where I then have my ID that a stock
- 00:11:10 underscore ID where I have my product
- 00:11:14 that is dark Product ID so just excuse
- 00:11:19 me just product what we got here suggest
- 00:11:21 product and when we didn't got the
- 00:11:23 quantity which is stock quantity and
- 00:11:25 where we then pass our request object
- 00:11:28 and where we now can use dark underscore
- 00:11:31 ID like this now if we save this we get
- 00:11:36 the same logic as an hour
- 00:11:37 product routes I get this more
- 00:11:39 structured response with more
- 00:11:41 information the same makes sense for
- 00:11:43 creating a new object here if we
- 00:11:47 succeeded I might want to return a
- 00:11:50 message where I say order stored and
- 00:11:54 where I then pass a URL where you can
- 00:11:57 get more information like the type here
- 00:12:00 you could then set the type to get and
- 00:12:04 the URL would essentially be the same as
- 00:12:06 we use up here so I can just copy that
- 00:12:09 URL down to this return a response here
- 00:12:15 access it on the result I get this is
- 00:12:18 the object that was created and of
- 00:12:20 course also maybe have a created order
- 00:12:25 object here that holds the information
- 00:12:28 about the created order and there we
- 00:12:30 could output the ID from result ID the
- 00:12:34 product from result product and of
- 00:12:38 course the quantity from result quantity
- 00:12:41 and if we tried that out by creating a
- 00:12:43 new order by sending a post request to
- 00:12:45 orders with the body we used before but
- 00:12:49 maybe with a valid product ID so let's
- 00:12:53 reuse that product ID we already used in
- 00:12:55 the past if you do it like this product
- 00:12:59 ID should be the key name here then we
- 00:13:01 store this and we see that nice a
- 00:13:03 response by the way if we pass an
- 00:13:06 invalid ID for a product doesn't exist
- 00:13:09 we also store debt so this also works
- 00:13:15 now we're storing a bunch of orders on
- 00:13:18 products that don't actually exist in
- 00:13:21 the database so that is something we'll
- 00:13:23 have to make sure that it doesn't happen
- 00:13:25 so let's turn or let's add some logic to
- 00:13:30 control this we want to make sure that
- 00:13:33 we can't create new orders for products
- 00:13:36 for products we don't have so here in
- 00:13:38 our post method in the orders J's file
- 00:13:41 now first of all check if we actually do
- 00:13:45 have a product for a given ID now to do
- 00:13:48 that I'll import product here
- 00:13:51 by calling required and going to the
- 00:13:55 models folder and then the product file
- 00:13:56 and then I can use a static method on
- 00:14:00 product here call find PID and search
- 00:14:05 for a request body Product ID and now
- 00:14:11 what I can do is if this succeeds then I
- 00:14:16 want to continue with creating the order
- 00:14:18 otherwise I want to return an error here
- 00:14:22 I will simply return a response with
- 00:14:25 status code 500 where I return an object
- 00:14:29 where I say message product not found
- 00:14:32 and as always you could all return some
- 00:14:34 machine readable error code here and
- 00:14:37 pass the detailed error object now in
- 00:14:41 the Dan block here I'll actually get my
- 00:14:44 product or my document but it is a
- 00:14:47 product let's knit maybe name it like
- 00:14:49 this and then in here I can execute the
- 00:14:52 average code here to store that new to
- 00:14:58 store it that new order however now we
- 00:15:00 have a nice level of nesting which we
- 00:15:02 don't want when using promises so what
- 00:15:05 we can do is I can simply return orders
- 00:15:08 safe which does return a promise as you
- 00:15:11 see and then I can use that the ban and
- 00:15:20 the catch blocks and chain them after
- 00:15:22 here just means that this catch block
- 00:15:25 here isn't too useful anymore because
- 00:15:27 it'll never execute because we have this
- 00:15:30 outer catch block here so now with that
- 00:15:32 we got a set up where I check if we do
- 00:15:35 have a product before I then try to save
- 00:15:38 it and we then execute all the our steps
- 00:15:41 for creating the order and for it
- 00:15:43 returning a response and if we don't
- 00:15:45 have our product here well then we'll
- 00:15:48 get into disk catch block where we
- 00:15:50 return the error anyways
- 00:15:51 so now with that if we save this and we
- 00:15:53 try again to create a product which has
- 00:15:56 an invalid ID it still succeeds do you
- 00:16:00 know why well remember what we did in
- 00:16:03 that products jeaious
- 00:16:04 file there we also check we do find a
- 00:16:07 product here but we check this in a
- 00:16:12 success case because remember if you
- 00:16:14 pass an ID for which no product is found
- 00:16:18 then you don't get an error
- 00:16:20 you just get null for product in this
- 00:16:23 case so actually we want to check that
- 00:16:26 if product if not product which means if
- 00:16:30 it's knowledge then we want to return
- 00:16:32 the response where we set the status
- 00:16:34 code to 404 and where we then can return
- 00:16:37 our response where we may be say message
- 00:16:40 product not found and yeah that's it and
- 00:16:44 since I returned all subsequent code
- 00:16:47 won't be executed in this case now with
- 00:16:49 dad if I send this now I get product not
- 00:16:53 found here and with that again if I do
- 00:16:57 get all my products and I dare for fetch
- 00:17:04 a valid ID of a product and I now create
- 00:17:07 a post request to orders again and I do
- 00:17:10 use such a valid ID it again succeeds so
- 00:17:14 this works but we can't store orders for
- 00:17:17 products we don't have which of course
- 00:17:18 is very useful now let's continue let's
- 00:17:21 get information about the individual
- 00:17:23 order well there we can't just use
- 00:17:25 product excuse me not product order and
- 00:17:27 call find by ID and pass request parents
- 00:17:33 order ID as an argument because we named
- 00:17:36 it order ID here call X sector turn it
- 00:17:40 into a real promise and then use catch
- 00:17:42 and then again and here we'll get our
- 00:17:45 document
- 00:17:45 you couldn't alter name it order because
- 00:17:47 it is an order whatever you want
- 00:17:48 and you can return a response with
- 00:17:51 status code 200 and then there a JSON
- 00:17:55 object where again we can pass our
- 00:17:59 information about the object we got so
- 00:18:03 there we could now return order and that
- 00:18:09 is equal to the order argument we get
- 00:18:11 here from Mongoose and add our request
- 00:18:14 metadata object where we can say yeah
- 00:18:17 you can
- 00:18:18 course send us a get request maybe to
- 00:18:21 the following URL to get all requests so
- 00:18:24 the URL would be H via local host 3000
- 00:18:28 slash orders to get all the orders and
- 00:18:31 that's just one example we could use as
- 00:18:32 always and that here in the error case I
- 00:18:35 want to return a response with status
- 00:18:37 killed 500 where we use Jason to of
- 00:18:41 course all returned an object where I as
- 00:18:44 always wrap this error in my error
- 00:18:47 property now with that finally for all
- 00:18:51 let's try it out I guess
- 00:18:52 so let's first of all copy that order ID
- 00:18:56 we have here and let's try a get request
- 00:19:01 to this order ID and there we go now we
- 00:19:06 also get that link to fetch all orders
- 00:19:09 we can click on that and we see all the
- 00:19:11 orders we created quite a bit so time
- 00:19:14 for a delete function and let's work on
- 00:19:16 that delete function here there again I
- 00:19:19 can use order and then remove and pass
- 00:19:23 an object to identify what I want to
- 00:19:25 remove I want to remove that object that
- 00:19:27 has an ID that matches my request
- 00:19:30 parents order ID property because again
- 00:19:34 I named it order ID here and then I can
- 00:19:37 chain x''k then and catch and a so often
- 00:19:43 in catch I just want to return my error
- 00:19:46 so I'm going to copy that code and you
- 00:19:48 could of course also outsource this into
- 00:19:50 some general function you then always
- 00:19:52 call that returns this response and in
- 00:19:55 the Dan block I want to return a
- 00:19:58 response so I get my result here won't
- 00:20:01 return a response where I essentially
- 00:20:03 have a message that could be order
- 00:20:06 deleted and the request could suggest to
- 00:20:11 create a new order by posting to orders
- 00:20:14 and there we needed to send an object a
- 00:20:17 body where we will pass the product ID
- 00:20:22 which will be an ID whatever type you
- 00:20:26 want to describe here and then
- 00:20:31 quantity which is a number something
- 00:20:37 like that now if we do that let's again
- 00:20:40 get all orders because we certainly have
- 00:20:43 too many and then let's fetch one of the
- 00:20:47 orders maybe this one and let's now try
- 00:20:50 sending a delete request to this
- 00:20:52 endpoint if I do that the order was
- 00:20:55 deleted indeed and if I try to get
- 00:20:59 information for that order it fails
- 00:21:01 orders now because we don't find it now
- 00:21:04 that of course means that in our get
- 00:21:06 requests here where we find by ID I
- 00:21:09 should also add a if check if not order
- 00:21:12 so if it's null as is in the example I
- 00:21:14 just ran was then I probably want to
- 00:21:18 return a response where I set the status
- 00:21:20 code to 404 and return an object where I
- 00:21:23 say message order not found so that I
- 00:21:29 avoid having cases like this where I get
- 00:21:32 order now which you could use if that's
- 00:21:34 your requirement but I don't want it now
- 00:21:37 if I try to fetch details for an order
- 00:21:39 that doesn't exist I get a 404 error and
- 00:21:42 that again was a lot of work but now we
- 00:21:45 worked on the orders side queue we're
- 00:21:48 storing that relation now querying this
- 00:21:51 relation and in general now working with
- 00:21:53 all that data and seeing how we can
- 00:21:56 further evolve our restful api is what
- 00:21:59 we'll dive into next