Setting Up a NestJS POST Request
Alright guys, so we are back with our third episode. In this episode, I'm going to show you guys how to use NestJS to set up a POST request. So we're going to do it in SJ, so you might probably have a good idea of how to do it already, but I'll show you how I'm going to do it. Okay.
So what we're going to do is we're going to use the post request decorator. So that's just post, and you can specify the path.
Now, one more thing that I want to do, and this I actually did this off-camera, but inside the main.ts file, I'm going to call app.setGlobalPrefix() and provide this /api prefix. What this will do is it'll just prefix all of our routes with /api. I recommend doing that because it's easier for you to set up your web server if you're using Nginx, for example. And yeah, it's really, really, really, really helpful. Okay, so now when we call an API, we're going to have to specify that, we're going to have to prefix it with /api.
Introducing Data Transfer Objects (DTOs)
Anyways, so for the path, I'm just going to go ahead and call this, uh, let's see, create. So when we call this endpoint, it's going to be /api/customers/create. So I'm going to call this createCustomer.
Now if you've obviously used Express before, you'll know that we can actually get the request body from the request object, and that's definitely true. So for example, if you wanted to use the @Request decorator, and if you wanted to just simply reference req.body, you can do that. Now, you won't get IntelliSense on the request body. You could create like an interface to type annotate that. Okay, but there's actually a better way that we can handle request payloads or request bodies in SJS. I'm gonna show you guys how to do that.
Understanding Data Transfer Objects (DTOs)
So, what we're going to do is we're going to use something called a DTO, and DTO stands for Data Transfer Object. Okay? And what a Data Transfer Object is, you can think of it like a schema. And it defines how the data is going to be sent over the network. So network as in like from the, from one place to another place. So if we're sending it from the client to the server, right, if we're sending across network, we want to ensure that this data that we're receiving matches this DTO. We want to make sure this is what that data represents. Okay?
Now, one thing according to documentation is that we can actually use classes to define our DTO schemas. They also do mention that you can also use interfaces, but they discuss why they recommend classes, primarily because classes are already part of the ES6 standard and they're preserved as real entities. Okay, because in ES6, in like in ES6, there's actually no interfaces, okay, which makes sense, right? There's no, there are classes, but no interfaces. Okay?
And so since the interfaces are removed during the transpilation, Nest cannot refer to them at runtime. Okay? And so you might not really notice many problems, but maybe until when you use things like Pipes and stuff, you might run into some problems with that. But like I said, we're going to use classes because that is what the documentation recommends, and because of that sole reason as well.
Creating the CreateCustomer DTO
Okay, so let's go ahead and create a Data Transfer Object. So what I'm going to do is I'll create a new folder called dtos. And we're going to have many DTOs. We'll create one DTO for now. So we'll call this create-customer.dto.ts. And we'll just simply create a class called CreateCustomerDto. Okay? And literally, this is just going to be a schema of how the request body is going to look like. So you're going to ask yourself, well, what is it that we're going to be sending over the network that will reach our server?
Well, when you create a customer, think about what are the details that you need. You'll probably need an email address, right? So we'll do email and we'll type-annotate that to be a string. Now for now, just for this tutorial, I am actually going to provide the id as well, as well as, I'm going to actually omit the date property. So let me actually get rid of date real quick. And we'll do instead of email, we'll do something like name. So we'll do Danny. Okay, I have an Adam, and name Spencer. And we really should be creating an interface to type-annotate our data, just to be consistent. Okay?
So I have my DTO. So like I said, the DTO should not look exactly, or it won't always look exactly like your actual model, like the actual record that might live in the database, because there might be other things in the DTO that you might need to determine how we should create the resource. Okay? So that's very important to note.
Using the @Body Decorator with a DTO
Okay, so what I'm going to do is we're going to go back to this createCustomer function, and we're going to use our DTO. So the way we can use this DTO is we can use the Body decorator. And this basically represents the request body. What this does is it literally extracts the entire body object from the request object and it will just populate it. Okay? And it also gives us, we can also type-annotate this as well. So I can call this createCustomerDto, CreateCustomerDto, type-annotate it like that.
So now if I were to reference createCustomerDto, whoops, createCustomerDto, you can see that I have the email, id, and name over there with IntelliSense. Okay. Let me go ahead and just simply console.log this. And let's go ahead and go into Postman. So I already have Postman set up already. And I'm going to go ahead and pass the id as well as the name, so we'll just do handsome anson, and I'll send that over there. And you can see in the console we should just have the, we should just have all that stuff logged. There we go. So cool. Now we have our DTO, we can actually use this to do whatever we want. We can use this to create the resource, use it to delete something, whatever it is that we want.
Integrating with the Service Layer
Okay, so that's pretty awesome. So, typically what we could do is we can create a method on our service. So I can go into our CustomerService, and what I can do is I can say createCustomer. And what I'm going to do is I'm going to say customerDto, customerDtos like this. Okay. And what I'm going to do is I'm just going to say this.users, and I think what we could do is we can actually, um, let me see. I should just be able to just push. Yeah, there we go. There we go. Okay, awesome.
So let me go ahead and actually zoom out a little bit, because I know it's a little bit hard to see everything. Now, let me do one more thing. Let me actually create, um, let me actually create a folder called types. Actually, let me do that inside customers. Okay. And what I'm going to do is I'm just going to call this customer.d.ts. And this will just be an interface, and this will just represent our customers. So id number, email string, name string. Like I said, I could use the DTO that we just created to annotate that, but this is supposed to represent like our actual model. It's just that we don't have any database set up yet. We will set that up later in the tutorial. Okay, but I don't want to use the DTO for that. Okay, let's do private, wait, did I call this users? It should be called customers. So private customers, type-annotate that with Customer, it'll be a customer array. And we'll just rename this to customers. Oh, yeah, customers. Okay. Now since the DTO has overlap with the Customer, they really is the same thing, just different names, it'll actually, it won't throw, it won't give us any linting error.
Testing the POST and GET Endpoints
Okay. So, so now that we have a route to create, let's go ahead and call this.customerService.createCustomer. We'll pass in the DTO. Okay? And that's it. So by default, like I said, it will return a 201. Okay. So, yeah, that's pretty much it. We don't have to worry about anything else. Okay?
So let's go ahead and actually create one route that will actually give us all the users. So I'll create that right up here. So get, or I keep saying users, I mean customers, sorry. So getAllCustomers. And what we'll do is we'll just return this.customerService, and we'll create a method called getCustomers. And we'll just create that right up here. So getCustomers, and we'll just literally just return just our customers like that.
All right. So let's go ahead and test out our endpoint. So everything is saved in memory right now. So let's go ahead and get the customers. So we have three customers. Okay, if I call POST, we have a 201 Created. And if I call that endpoint again, we should have our fourth, we should have our fourth user or customer in there. Okay, so that's pretty cool. Awesome.
The Need for Validation (Next Steps)
So, one more thing that we actually have a problem with, and we'll address this in the next video. So I'm going to show you guys how we can actually perform validation. And this is why you'll want to use DTOs. You'll never want to not use DTOs after I show you guys what we're going to do.
Well, right now, just to give you guys a sneak peek, if I actually don't provide the id, let me actually make a post request again. If I don't provide an ID, I can actually still make a post request, and you can see that it'll add that data to that array, but it's just missing id. And this is obviously really bad because your data isn't consistent. So you have some records that does not have required properties. I'm going to show you in the next video how we can actually validate our our request bodies. And we can actually send a 400 status code if the request payload or request body is invalid.
So that's gonna be pretty much it for this tutorial. And I will see you guys in the next video. Peace out.