Refactoring the Service for Dynamic Responses
All right, so let's just say for example you wanted more control with your API. Because right now, what we're doing is we're returning findCustomer, but obviously there's many different scenarios that could happen with your controller. The controller could return a 200 or a 400 status code, you never know, right? That's why we need to make sure that we are handling that appropriately.
So let's just say for example, instead of just returning a customer the whole time, let's say if the customer can't be found, what do we do then? We want to return a 404.
Here's what we're going to do. I'm going to go ahead and inside the service class, what I'm going to do is I'm going to go ahead and create an array. And I'll just call this users.
Okay, uh, private... yeah, there we go, that's better. Okay, so what we're gonna do is we're gonna go ahead and just put some users in here. So id this is one... there we go. Copy and paste it a couple times. So one... do Adam... and we'll just create one more and we'll do... uh... Spencer. Okay, that's good enough.
And what we're going to do is we're going to completely get rid of this findCustomer method and we're going to re-implement a new one. So findCustomer is going to take in an ID. The ID is going to be a numeric value, so data types could be number, so we'll type-annotate that.
And what we're going to do is we're going to basically just create a simple search for this user's array. So we basically want to find the appropriate user that matches this ID. Okay, well let me actually call this findCustomerById. Okay.
Now what we want to do is we're going to return users.find(), and we're going to pass in a predicate. So we want to find it by the ID. So we'll pass in a predicate where we're going to check to see if user.id is equal to id. So if the user is found, it'll actually return that record from the array. If it's not found, it should just return undefined.
So what we'll do is we'll actually modify this endpoint so that what we'll do is we'll actually expect the customer, or expect the user who's using this API to give us an ID. Okay.
Handling Requests the Express.js Way
So, we have the ID, so the :id. It's very similar to Express when it comes to route parameters. You just denote it with a colon.
And to actually get the route parameter, you can actually do it the Express way. So what you can actually do is you can actually use the @Request() decorator. And we'll call this req, and you can actually type-annotate this request object using the Request interface that comes from Express. You can see that it literally comes from Express. And we can also use the @Response() decorator and we can type-annotate the response object using the Response interface from Express as well.
Okay, and so this actually can give you more control with the request. So for example, you can actually literally do the same thing that you do in Express. You can reference the request body, you can reference the headers, you can reference the cookies, whatever it is that you want. If you want to get the params, you can do req.params. Okay? If you want to send a 200 status code, you could do res.send(200). If you want to send a 403, you can do it as well. So this is a way how you can get more control with your controller.
Okay, so let's go ahead and do this real quick. So what we want to do essentially is we want to actually get the route parameter. Now there's many ways that we can do that. We can do it the Express way by doing req.params and then reference id. But let's do it the NestJS way.
Using the @Param Decorator and Pipes
So to do it the NestJS way, we can use the @Param() decorator. Okay, and what we do is we specify the name of the parameter, so it's id, without the colon, inside the parentheses of Param. Okay, and then what we can do is we can give it a name, so id, and we can specify the type.
Now, by default, it's actually going to be a string. So if I were to actually pass an ID... oh well, since we're type-annotating it, that's fine, because it will do the conversion. But if you actually were to do typeof id, you'll see that in the console it should log string. So what I'm going to do is I'm going to go ahead and actually just comment this out for now. And I just want to show you guys how this works. If I refresh, it should just say "cannot GET," but if I pass in an ID and if I look at the console, you'll see that it says string.
Okay, now, one thing that I'm going to just cover really quickly is you can actually use what's called a pipe. And we'll go more in-depth in this in the next several tutorials on what pipes are. But if you pass in ParseIntPipe, what this will do is it will literally transform the data type, the value, like the data-type value of this id from a string to a number. Okay, so you'll actually get a number. So even though you are type-annotating this as a number, it's actually not really a number. Okay? So to be safe, you should definitely use the ParseIntPipe. But don't worry so much about that right now because we'll go more in-depth about pipes later. So now you can see that it actually logs number, so that's good. Cool.
Implementing Logic with Express Objects
So let's go ahead and what we'll do is we will call findCustomerById. Now typically this should be some asynchronous function, but right now since we are, we don't really have any asynchronous operations, we will not worry about that for now. So this is all synchronous. OK?
So what I'll do is I'll just go ahead and do const customer = this.customerService.findCustomerById. And at the controller level, what we could do is we can say, if (customer) was found, okay, we can go ahead and call res.send(customer). By default it should just give us a 200 status code. Let's say the customer was not found. We can go ahead and actually send a... let's set the status to 400 and we can send a message saying "Customer not found." Let's do, uh, yeah, let's do 400, that's good enough.
Okay, so now if I go over here, if I refresh you can see that we found the customer. If I pass in an ID that actually is not in that array, you can see that it says "Customer not found." So literally we can do this with NestJS. This is kind of doing this very closely, it's very similar to how you do it in Express. I'm going to show you guys how we can do this using the NestJS way.
Refactoring to the NestJS Way with HttpException
Okay. So let me go ahead and create another route. And this will just be the same logic, we're just gonna... I'm just going to show you guys how I would do it in the NestJS way. Let's go ahead and just call this route customers/search and we'll have that ID parameter. And I'll call this searchCustomerById. Okay.
So first off, we actually don't even need this request object. So just having this is actually kind of useless. And also when you test your code, when you unit test, you're going to also need to mock another request object or stub it out. So it adds like extra stuff that you have to add into your your controllers. So one of the nice things with Nest is that we actually, instead of using like the request object, we don't necessarily need it all the time unless we really need it. But most of the time you probably won't ever really need the request object, and we'll get into that later.
What I'm going to do is I'm going to go ahead and use the same Param decorator. I'm going to pass in the name of the parameter, id, and we're going to use the ParseIntPipe to transform this into the correct data type. Okay. Um, and what we're going to do is we're going to go ahead and find the customer. Okay. So what we'll do is we'll say const customer = this.customerService.findCustomerById. And what I'm going to do is, let's say, for example, if the customer has been found, we'll return the customer. Okay?
Now, one thing to note that is if you actually use the request or the Request and the Response objects in your controller, you actually need to use those to send the response back. You cannot just return the object. Okay? So if I actually, for example, say for example if I have the Response object here, I need to actually explicitly call res.send. I can't just do return customer. Okay? So, but over here, since we're not using the Request or the Response object, we can—or we're not using either one of them—we can actually just return the customer.
If the customer is not found, we can actually just throw a new HttpException. And what this will do is this will allow us to actually send a response back to user without actually using the response object itself. So we can give a message, so let's just say, "Customer not found," and we can give it a status code. So you can hardcode the status code to 400 if you want, but NestJS has an enum called HttpStatus and it has a bunch of different status codes. Okay? So the one that we want is probably, say, BAD_REQUEST.
All right, so let's go ahead and test out this endpoint. And so right now, this one, this is the endpoint that we had from before. Let's add customers/search. So you can see that this one, it says "Validation failed (numeric string is expected)." Okay, so let's go ahead and provide a string. So you can see that we get a customer back, so that logic is working just fine. Let's go ahead and pass in four and you're going to see that it gives us a status code of 400. But now if we look at the network tab, you can see that it in fact actually does give us a 400, which is very useful for libraries such as Fetch and Axios, because by default, they will actually reject the promise if there's a bad request or a status code of 400, 500. Okay, so that's good. If I go into the network tab, you can see that if I refresh, we have a 400 request, and that is very good.
Conclusion and Next Steps
So that is going to be pretty much it for this tutorial. I just want to show you guys how we can implement this the NestJS way. So instead of having the request and response parameters, you can just literally avoid that, get the actual path variable, find the customer, return the customer if it's found or throw an error, and that will actually give us back the appropriate status code to the client. Okay?
So that's to be pretty much it for this tutorial. So in the next tutorial, I'm going to show you guys how we can implement the rest of the other, you know, methods for HTTP such as POST, PUT, PATCH, DELETE. They're very simple, nothing too complicated. And then we're going to play around a little bit more with controllers and services just so that you guys can understand a lot better. And then we'll move into the more advanced stuff and the more cool stuff that NestJS has to offer. So I'll see you guys in that video. Peace out.