Check out a free preview of the full Introduction to Node.js, v2 course:
The "Creating a Low-Level Server" Lesson is part of the full, Introduction to Node.js, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Scott uses the http module to create a simple web server. The createServer method is passed a function to handle each request and response for the server. The request object contains information about the request along with any data sent to the server. The response object returns a status code and any results after processing the request.

Get Unlimited Access Now

Transcript from the "Creating a Low-Level Server" Lesson

>> Cool, let's hop back on to the next thing that we're gonna talk about, which I think is the reason why a lot of folks are here. Servers, APIs, how do we do the thing in Node? Show us the promise land. Okay, this is it, I wanted to save this one close to last because obviously there's a lot of stuff you need to know before you can advantage of it.

[00:00:21] So we are gonna talk about servers and how you could build servers in Node. And the many different ways you can do it, and then pretty much my recommended way. But we won't be going into detail how to build some ridiculously nice CRUD API that talks to databases and stuff like that.

[00:00:35] Because we already have courses for that on Frontend Masters that I highly recommend you check out after this course if you're really interested in how to build a modern API in Node.js today. So check those out if you're interested. But this course, in this section, we will be talking about, at least the fundamentals behind it, how you could do it natively and how you should do it with something else.

[00:00:55] So Node.js has access to OS-level functionality like networking tools. So when you're starting a server on a machine, whether it's your computer, whether it's Amazon somewhere, whatever machine is on, it's accessing the network tools exposed from the operating system to whatever language you're working with. So, in this case, it's exposing it to C++, which is exposing it to JavaScript through, in this case, something called the HTTP module.

[00:01:25] So we're actually interacting with the operating system through Node. And I think that's just really interesting if you think about it. You're literally talking to the operating system through JavaScript. And if that's just not powerful, I don't know what is, especially seeing where JavaScript started as changing colors of hyperlinks and things like that.

[00:01:43] [LAUGH] I guess it's just crazy to think that you can do that now. But yeah, it has access to stuff like that. And that's why a lot of people use Node, because it is JavaScript, right, but it does have access to the OS-level things. But also because when it comes to APIs and stuff, Node is, and I talked about this a little bit, it's single threaded.

[00:02:04] And if you don't really know what a thread is, you can think of a thread as a block. So if you have a CPU with eight cores, right, you could think of one thread taking up one core. So you can do eight different things at once if you took up all eight cores, whereas JavaScript only ever take one of those cores, one of those threads.

[00:02:21] And it basically uses an event loop, which has a different worker thread separate from the thread that your code is working on. And it will schedule different things. And it will just keep looping over and over to see if some work needs to be done. And when that work is done, it'll basically notify you when it's done with a callback, which can be translated to a promise, which can be translated to async/await.

[00:02:46] And that's where all that stuff comes from. So this basically allows us to continue receiving requests from our users and our customers at the same time we're scheduling more work to be done and process in the background. Whereas other languages, it's blocking. So you might have requests coming in and they're being processed in kinda like a serial manner.

[00:03:06] That doesn't mean they're slow. They're not slow, they just are blocking, they have to have access to multiple threads. The other thing is Node can have access to multiple threads. Just by default, it doesn't, but you could use the, I forgot the name of the module, I wanna say it's the OS module, maybe it's the core module.

[00:03:23] I can't remember, they changed it, I can look it up. But you can use that module, or even a third party module is probably better than using the internal one, to actually spawn different amounts of work into different threads. Which is perfect for something like load balancing when you're deploying a server somewhere.

[00:03:40] So you can access multiple threads, just by default it won't do that for you. It's a manual thing that you have to set up, and you have to manage that yourself. It is not managed internally via Node like other languages are, so just keep mind of that. That was a lot, but I needed to get that out.

[00:03:57] So let's talk about servers, now that we know why servers are good in Node. Node does ship with an http module. Like I said, it's an abstraction around OS-level networking tools. But some people might laugh when they hear this, but http module is low level. And the reason I say some people might laugh, cuz it's like literally nothing in Node is low level compared to other languages.

[00:04:20] But compared to other stuff in Node, yes, the http module is very low level, and you should probably never use it. I can't think of any good example why you would ever use the http module, but I'm gonna show you how to make a server using it. Not because I want you to, it's because I want you to see the difference between using this versus installing something.

[00:04:40] And how better it is to leverage something from the community versus doing something from scratch. So what we're gonna do is we're going to build this http server here from scratch. So let's do that. I'm gonna make a new, Let me get rid of this thing. Go away, I'll make a new folder here.

[00:05:03] We're gonna call this server. And we'll make a index.mjs. And I want to import http from 'http'. This is included in Node. So I don't have to install anything, we're good to go. And if you were to look at that, you'll see that there's just a bunch of stuff on there, networking things.

[00:05:28] It's stuff you will pretty much never use, but that's why I said it's pretty much low level. So you can follow along with me or you can copy and paste this code here, but I'm gonna walk through it so we can kinda see how it goes. So the first thing is we need to just make some variables here.

[00:05:43] I'm gonna make one for the host, which is gonna be localhost. If you don't have a localhost is, it's your machine. It's a reference to the IP address of your machine, which is usually something like That's pretty much the IP to your local machine, but you could reference it as localhost.

[00:06:06] The local DNS lookup on your computer understands that localhost points to that address that I just put the IP address. So localhost is great. The next thing we want is a port. If you don't know what a port is, you can think of a port as the doorway to your host.

[00:06:24] It's how you get to the host. Most websites that are deployed on the Internet have a host of 8080 or 8000, mostly 8080 or 80. It's just that the browsers don't show them, but they're there. They're all there, you just don't see them because they all have the same port.

[00:06:39] So why even show it, they just exclude it from the URL, but it's definitely there. But locally, we have to specify a port. So you can think of it as every time you make a server on your localhost on your machine, you need to put each one on its own port, because a port can only be occupied by one server and not many.

[00:06:58] So if you try to start another server on the same port, you'll get an error saying, this is already being used, try something else. And then sometimes you'll have different programs on your computer that open up ports without you knowing. So if you download, I don't know, some taskbar app up here, it's probably using a port on your computer you don't even realize it, like Dropbox, Sync or Spotify.

[00:07:16] Who knows, it could be using a port and you might not know until later. So most time ports are four numbers, sometimes they're five, but we're gonna use 8000 for now. Sometimes you'll see 3000 and things like that, we'll just use 8000. And then the next thing is we're gonna create a server.

[00:07:33] And that is, we can say, http.createServer, like this. And it takes in a callback function, which is going to be the handler. How do you handle the incoming request from the server? So we're gonna say, okay, and then that callback receives two arguments. The first one is gonna be the incoming request, I'm just gonna put req for short.

[00:07:55] And the next one is gonna be the outgoing response, and I'm gonna put res for short. Because the way servers work is that they handle requests and then they respond to them, and that's why I guess that's why they're called servers. I don't know if they modeled the name after restaurants, [LAUGH] or whatever, but it's a server, it serves the clients.

[00:08:12] So you receive a request and then you respond to it in the fastest way you can. So we have our request and response here. And then the next thing you wanna do, if you think about a server, there's typically different paths. So if you ever went to a URL, like some, and you go to slash somewhere else, it shows you two different things.

[00:08:33] You get two different pieces of data, whether it's a website or an API. Those are two different paths, but you also have different verbs. So you might have the same path, like /todo, but one might be a POST and one might be a GET. So either you have a route on the server as a combination of a path and a verb.

[00:08:51] And that would be a route, so you can have many routes on your server. So what we're gonna do is we're gonna create some routes and do different functionality depending on what those routes are, and we'll try to figure it out from there. So what we'll say is, if req., what is it, path?

[00:09:13] See, I don't remember, now I gotta, method, there we go. So if req.method is a POST, So if someone's doing a POST request here, that means they're trying to send some serer to the data, we wanna grab that data and we wanna put it somewhere. So let's do that.

[00:09:28] First, let's create a little, I don't know, database, I guess, of the things that, actually, no, we'll do that on the next one. So we'll say POST, and then what we need to do is, because this is very raw, and even if you send JSON up, we're not gonna receive it as JSON.

[00:09:47] We're gonna receive it as a buffer. Like when we were reading the file and it was all those little bits, that's how we're gonna receive the data. That's not very useful for us. And it's not gonna come at once, right? It's being streamed to us in little bits, so we have to collect all of them and then put them together.

[00:10:04] And then we'll have the data that the client intended to send us. So we need to do that, we need to collect that. So I'm gonna say, body = ''. Then I need to register some events on the request object that fire whenever a new piece of data comes in.

[00:10:22] And then send that data over to the body to be collected, which we could then look at. So I'm gonna say, req.on, ('data'). And this is going to get what we call a chunk, literally a chunk of data. And I'm gonna say, body += chunk. So we got that.

[00:10:46] The next thing I'm gonna do is say, req.on('close'). So when it's done, there's no more data coming in. We should be good to go. This is where you would insert into a database, right? We got the data, we're good to go. We can insert into the database. I'm just gonna log this so we can see what it is.

[00:11:06] So we'll say body. So if it's a POST, we'll see that. And then else, if it's not a POST, if it's just a GET request, then we will just do a res, or, I'm sorry, actually before the GET request, we need to close this response. Yeah, I was about to run into a big mistake.

[00:11:25] If you don't respond back to the client, your server's gonna be hanging. As in, it's gonna be stuck in this state of I received the request but I don't know what to do with it. And then the connection is gonna be stuck open. And a client's gonna be like, I'm waiting to hear back from you, you haven't gotten back to me yet.

[00:11:42] And depending on the client, whether it's a browser or whatever, there's a certain timeout limit, eventually it'll just timeout. So if you ever seen a timeout error, it could have been someone who forgot to send back a response, or more probably, what actually really happened is the response is just taking too long.

[00:11:59] But sometimes yeah, somebody probably forgot to respond back. And that's what would happen here if I would have moved on. So we do need to close off this branch here by responding. So I'm just gonna say, the quickest way is to do res.end, and then you can send back something like that.

[00:12:13] But I'm also just gonna say, res.setHeader, or no, I'm gonna say set, no, which one do I wanna do? writeHead, I think it's writeHead, yeah, I wanna do a writeHead. So this is writing the header, the status code. So for a successful POST, it's usually a 201 status code.

[00:12:34] If you don't know much about status codes, it's totally fine, you can look them up whatever you want. But in today's world, especially with GraphQL, status codes are becoming less and less of a thing. But it is very helpful to understand the different status codes, it'll probably take you 20 minutes just to read them and figure them out.

[00:12:48] But a 200 status code means successful GET. 201 means successful POST. So we're gonna do a 201. And then else, if you didn't POST, you just did a regular GET, we'll just say res.end. And then we'll say, hi, like that. And I'll also just say res.writeHead(200), for a successful GET, like that.

[00:13:13] So now we have our server. The next thing we need to do is we need to listen for the server. We have to turn it on to the actual ports and stuff like that. So we'll say, server.listen. And you can see here, it takes a port, we have a port.

[00:13:29] It takes a host, we have a host. And then the last thing we could do is we could put a callback here to do something. And we'll say, console.log('Server on'), and then let's do some template strings here. We'll say ${host}:, ${port}. There we go. So we'll try that, and see if I didn't mess anything up, we will see this server run.

[00:14:06] So let's give it a try. So I can say, node, and where did I put this, server/index.js. So I can say, node server. I gotta back out, there we go. Actually, I'm just gonna go into that file. So that folder, there we go. And I can say, node index.js.

[00:14:27] And as you can see, we are on localhost:8000. So that log works. It's logging server on localhost:8000. And notice my Node app is still running. Up to this point, we would run a command, it would execute and then it would stop and exit and we'd be back in the terminal.

[00:14:46] Right now, it's just sitting here, it's just waiting. Because we're connected, as far as Node's understanding is, there's still some work to be done, there's still some work to be processed. So it's just continuing along and waiting. And that's the difference between a server and a CLI, where we ran one command and it was done.

[00:15:04] Or we did the file reading, we ran one command and it was done. The server just stays on, that's the whole point. So unless you're doing something like serverless, which is something that doesn't stay on but still operates in the server context, you'd probably want your servers on all the time.

[00:15:20] So that's what this is doing. It's just sitting here until it errors out or when we stop it manually, which you could do with Ctrl+C. So I'm gonna keep that running. And then I'm gonna interact with this API to see how it works.