REST & GraphQL API Design in Node.js, v2 (using Express & MongoDB) Controllers & Responding
This course has been updated! We now recommend you take the API Design in Node.js, v4 course.
Transcript from the "Controllers & Responding" Lesson
>> Scott Moss: So controllers and responding. And so far we really haven't talked about controllers. The only responding we looked at was rest.json. I didn't tell you anything more, intentionally. So basically, controllers, they have access to the incoming requests. Without that they would be kind of pointless. [LAUGH] So every time there's a request made, that function that we passed into that path, that eventually, you know.
[00:00:26] We have like those sub routers and those sub routers, and eventually have the function. That function right there is the controller, it has like I said access to the request we can see right here. It's got the request It's got the response object that express gives us and then it has this next thing.
[00:00:42] And that's optional, we'll talk about the next thing. We are gonna be reusing controllers because we're doing reqs right now and, like I said, it's the exact same thing for every single resource that we have. You can tell by looking at the last exercise you just did. All this is the same on every single route.
[00:00:59] So therefore, the controls are probably gonna be the same too. So why recreate them? We're just gonna have one file that takes in the resource and does it. The controllers should be async.
>> Scott Moss: I guess it could be synchronous, but you should keep them asynchronous. So the difference, okay, let me explain it like this.
[00:01:21] The whole thing about note is, it's an asynchronous architecture. It's event driven, it's a really good for concurrency and stuff like that. But if you use a lot of like, taxing synchronous code, you're going to block. You're gonna block note, right. So for instance, if somebody comes in with a request, and all a sudden, you do a I don't know, a wild loop or something like that.
[00:01:43] You're gonna block the whole process, right? Nothing's gonna work, so you kinda wanna keep things a-synch and that's down to like logging. You don't even want to log synchronous because you might think, it's just a console.log. Okay, well let's say you've got a console.log inside of a request with 10,000 I don't know 5000 requests per second or something like that.
[00:02:00] That cause a log is gonna be blocking, every time. And eventually is gonna build up and you probably will have like a crash. So, I've actually, that's actually happened before. I worked with some clients and they cannot figure out why they couldn't get pass a certain threshold on their load testing and it turns out that they had logs, logs.
[00:02:15] So instead of writing to the console we, wrote to a file instead. All right, and that sped it up dramatically. So you kinda wanna keep things async, try to stay away from synchronous code. But you can totally put synchronous code in there. It's not a big deal, especially if you're not doing anything async.
[00:02:30] Don't make it async if you're not doing anything async, you know what I mean? But don't do anything long-pulling like while loops and writing to files synchronously and reading files synchronously If you can do it async, do it async. Controls are composable, because at the end of the day, they're really just middleware.
[00:02:46] And again, we'll get to middleware, but you can reuse these things because they take in the same requirements, they take in the same response, and they also have the ability to pass control over to something else. And they can pretty much respond with anything, right? We've been responding with JSON But we can respond with a file, we can respond with an asset, we can respond with stream, buffer, we can pretty much respond with Express.
>> Speaker 2: And that's through the controller yeah.
>> Scott Moss: Actually responds is a controller, but technically nowhere can respond too. Like for instance, you're gonna see, it's already in there actually. But we have middleware that does authentication, and if it determines you're not authenticated, it will respond, versus sending it to the controller and letting it respond.
[00:03:36] It will stop it right there. Like no, you're not authenticated. I'm not letting you go any further. So let's just hop into some exercises right quick then. So if we go look inside that query JS, the first thing is this you'll notice this controllers method, and it's literally just doing nothing.
[00:03:55] It's just returning this test data. That's fine, this is for another example when we talk about databases. Forget about that, what you're gonna be working on is down here in these methods down here, where it says, createOne, updateOne, deleteOne, getOne, getAll, findByParam. We're not so concerned with the database query right now.
[00:04:13] We're really just concerned with responding. We're really concerned with use. So what we're gonna do is we're gonna assume That these controllers up here are functional. Right now they are not, they're just returning a promise, right? But let's assume that they're going to the database, they are doing something, right?
[00:04:31] So with that assumption, you want to consume these controller methods above inside these actual controllers given the model and the request. So if you scroll down I'm doing a little bit of meta programming here. Little bit of meta programming. Little bit of generation going on here. You can see that, I'm passing in the model.
[00:04:49] And so in this case this model might be the song model. It might be the playlist model. It might be user model for each one of those models I'm creating these controllers. Which are the results of these functions. These functions taken any model, they return the functions as it's crazy sometimes.
[00:05:09] This is a function returning another function as it is, looking like what is that two errors there it is just this the same thing.
>> Scott Moss: That's data, right? So, this thing takes in the model, creates a closer and returns the actual controller that allows me to do this, it allows me to take the model in like that.
[00:05:31] So, this returns a function that is eventually going to be the controller. So, [INAUDIBLE] using [INAUDIBLE] my coding. So basically what we're gonna have to do is, let's work on one of them. Let's work on createOne. So on createOne, like I said we're gonna take advantage of these controls up here.
[00:05:48] Assuming that they're done. There's a createOne method here, right. So what we're gonna do is we're gonna use the controllers at createOne, satisfy the arguments, which are the model and the body. And then just let it do its job and then respond appropriately. So we're gonna be using, right coming down here to createOne we're gonna say, okay, cool.
[00:06:05] So I know the model, I don't really care what the model is. I'm just gonna pass it in, so I'm gonna say, okay, cool. I want to controllers.createOne. And if we go look at the arguments for it, Takes in a model, and then the body. The body is the incoming body from the request, we're creating a resource, right?
[00:06:26] So we expect the client to send us the resource to create, right? How else would we create it? So we need that, so we're gonna pass in those two things and therefore it's gonna create it. So we have the model, it's right there And then, for the actual resource, that's going to be- I'm sorry.
[00:06:43] For the actual body, that's going to be on the request. Everything from the request is going to be on the request object. And that's actually going to be in a req.body by default that's not going to be there. We got ourselves some middleware. When we get some middleware, you see what that looks like.
[00:07:00] But basically, some middleware is gonna take the JSON that you post, and it's gonna put it there for us.
>> Scott Moss: Okay, and then what you wanna do, because this is a promise, you wanna go ahead and just return this. Actually, you don't really have to return if you don't want to, but the important thing is you need to resolve the promise like this, using the .then, you get the results.
[00:07:24] And then what do we want to do with the result? We want to go ahead and send it back and we're just going to assume it's JSON, so we say result like that.
>> Scott Moss: But let's say this errored out, and we want to catch the error. Something you can do right here in line, we'll get to error header later, but you could just go ahead and catch this error, and go you know what?
[00:07:47] I want to catch this error right here. Let's say you want to send back a server error. So you can do something like res.status. Change the error code or the scp to whatever you want. Then you can call it .send, which will send back a message.
>> Scott Moss: And then now you send back a message back to your client with a status of 500, that says things re not looking good.
>> Scott Moss: And because we're using meta-programming region generation, this one method will work for the create one of every single create one here. It's the exact same thing. They're all the same thing. If you want to verify you can go into the dot controller file of every appropriate resource and you can see that's what I'm generating right there.
[00:08:46] Generate controllers for user. Generate controllers for play list. Generate controllers for a song that's how I'm generating it. You could just come in here, and write them all one by one, if you want, it's totally fine. But they're all the same thing, so why not just generate it?
[00:09:03] This allows us to test easily, too. Now, all we have to do is test our, this code. Versus testing every single thing, which is the same thing, cool? So before I let y'all loose, I'm gonna talk more about some of the ways you can Respond to things. I would come in here and, if I were you, [INAUDIBLE] this request object and see what it looks like.
[00:09:25] There's a lot of crazy stuff on there, IP addresses, headers, all types of stuff. Let's talk about some of the responses you can do here. Lets just open up a playground of Let's go to this get one, we'll do some stuff in here. So, I'm not going to mess with the modeling thing right now, I'm just going to mess with some responses.
[00:09:43] So let's say I want to respond with just anything. I'm gonna do res.send hello. So res.send is something you probably haven't seen Basically, you can just send whatever you want, and then Express will try to figure out what it is. Like, all right. I'm just going to try to figure out what it is you're trying to send.
[00:10:02] Right now it's a string, so it's easy for it to figure it out. When you start putting complex stuff in there, you just don't use it. It's just like the catch all, here. Express, figure out what this thing is and send it, and do it appropriately res.send allows me to send stuff like that.
[00:10:16] You saw res.json allows you to send back anything, any object. That will encode it in json and it also sets it to application json, the headers, so it will do that.
>> Scott Moss: And then if you want to be able to control the status of a response before you send it, you could just do .status.
[00:10:40] For instance, on POST request, technically a POST request should be status 201, right? So before you respond to a POST request, you might change the status to 201. And then you can chain your appropriate method after it. Seem like, a res.status(201), and then json, and then pass in your json data here.
>> Scott Moss: All right, so that's some of the stuff you could do. You can also do sendFile, which you can give it a path to a file in your app and it will send that file back. So that's great for sending back HTML pages. It also ties into templating.
[00:11:12] So let's say you are using express to build a service site ap that uses handlebars or EJS and stuff like that, you would use this. Res.sin file or res.render or something like that, so you would do a lot of that stuff. So there is a lot of ways you would use express to respond well.
[00:11:30] Because we are building an AVI that's response adjacent. We are going to be doing this, almost every time Until it's an error. Like we're not sending back access. This isn't sending back images. This isn't sending back anything. We're literally just building an API, a JSON API, so most of our requests will be this.
[00:11:48] And the fact because it is, and we probably need different error messages to satisfy our client, most likely we'll build an abstraction around that response.json. That takes in a message and something like that, that way you can respond appropriately. You can have different types of responses for the JSON, but that's for production.