What This Is
The following is a brief explanation of how to set up (and run!) a server using NodeJS (from now on, called Node) and Node’s express
module. You’ll learn how to install Node, as well as how to download and include (‘require’ packages). By the end of this, you’ll have a simple webserver running that can be extended into multiple projects:
Prior Knowledge
To get the most out of this tutorial, you’ll need to know a few things beforehand:
- You’ll need to know basic HTML. While we won’t initially be doing anything on the front-end, we’ll eventually be sending some basic webpages forward.
- You may want to know some basic CSS. While it’s not hugely necessary, I’d also recommend beginning developers learn HTML and CSS together.
- You absolutely must know JavaScript. Since our server’s written in JavaScript, this tutorial will be largely useless/foreign to you if you don’t have a background in JavaScript.
- You’ll probably want to already know AJAX and asynchronous JavaScript. While I’ll do a brief review of that below, I’d strongly suggest that, if you have never heard of these terms before, or never written a callback, you stop right now and go do that first.
All good? Then let’s continue with that aforementioned review of Async JavaScript.
Review of Asynchronous JavaScript
Synchronous JavaScript
JavaScript is single-threaded. That means that it can only, generally speaking, do one thing at a time. In more simple programs, this doesn’t matter as much, since modern computers can run JavaScript so quickly that it feels like everything’s happening at once. However… (you knew that was coming, didn’t you?)
Asynchronous JavaScript
Certain actions in JavaScript are such that you don’t know when they’ll finish. We call these actions ‘asynchronous’, because we’re not sure if they’ll happen immediately after we ask them to, or if they’ll take some time. Here’s a brief example:
var data = null;
function getStuff(){
grabDataFromFarAway(function(data){
console.log('Inside the callback, the data is',data)
});
console.log('Outside the callback, the data is',data);
}();
console.log('Outside everything, the data is',data);
When we run this, the getStuff()
function is called. Now, imagine that this function grabs my favorite color (blue) from some external server. In normal, synchronous JavaScript, we’d expect the console to read
Inside the callback, the data is blue
Outside the callback, the data is blue
Outside everything, the data is blue
because that’s the order the lines are given in. However, JavaScript can’t know when the response from grabDataFromFarAway()
will return. It may take picoseconds, or it make take centuries! So instead, JavaScript does the sensible thing and continues onto the next line. As such, we get the following:
Outside the callback, the data is null
Outside everything, the data is null
Inside the callback, the data is blue
If you’re wondering what this ‘callback’ thing is, that’s the function that’s ‘called’ when the response gets ‘back’ from the asynchronous request. In this case, it’s just an anonymous function that logs out "Inside the callback, the data is "
plus the value of whatever we get back.
Async is Always Async!
If you’re using an asynchronous function, you can never expect it to run synchronously! Consider the following code:
console.log('one');
setTimeout(function(){
console.log('two');
},0);
console.log('three');
Before we answer, note that setTimeout()
is an asynchronous function that basically says “shove whatever’s in here aside, and run it after n seconds”. In this case, n is zero. The important thing about setTimeout()
- and indeed all async functions - is that JavaScript does NOT wait for it to be done, but instead moves onto the next statement and does whatever the async function says to do when it’s ready. So, what does the above code print out to the console?
You may assume that, because the delay time is zero (i.e., no delay), it prints:
one
two
three
But unfortunately, because setTimeout()
is an asynchronous function, JavaScript still says, “okay, I’ll deal with that later”, and moves onto the next line. So we get:
one
three
two
Moral of the story? Don’t expect even the briefest of async functions to run sequentially! Also, keep in mind that, in a series of nested functions, if one of the functions is asynchronous, all of its parent functions can be considered asynchronous.
Common Async Functions
The following are pretty much always run asynchronously:
- HTTP Requests: These requests (
get
to ‘get’ information, post
to send info to a server, as well as a few others) are generally used to bring external resources into a webpage or webserver. For example, when your favorite video sharing site plays a video, it might send out a request every few seconds to say “Hey, external resource! Can I get the next bit of video?”.
setTimeout()
and setInterval()
. These two sibling functions are asynchronous. As such, it’s important to remember that if you wanna use setTimeout()
as a timer, you’ll need whatever you want to happen at the end of that time to be inside setTimeout()
‘s callback.
- User-triggered events, like
window.onclick()
and window.onkeydown()
. As we can’t expect the user to hit a button exactly when we want, these actions are asynchronous. It’s not uncommon to see something like this:
window.onmousemove(function(e){
//e is the mousemove event object
console.log(e.x,e.y);//prints the current mouse coordinates
//do something with those!
});
So now hopefully you at least have some idea of asynchronous JavaScript. Again, if this is your first introduction to it, I’d suggest you take a break and read up on it a bit. Because next, we’re gonna go in a completely different direction and code editors (hah, you didn’t see that coming, did you?).
Code Editors: What to Use
To make your life easier when writing JavaScript (or HTML, CSS, C#, Assembly… Whatever), you’re going to want an Integrated Development Environment, or IDE. An IDE this a special program that developers use to write code. Now, you might be thinking “I can write stuff in Notepad!”, and while that’s true, you’re gonna have a difficult time if your code ends up being longer than a few files. IDEs allow you to do cool things like:
- Changing multiple lines at once
- Selecting (and changing) all instances of a word or phrase
- Having multiple files open at once for easy comparison
- Auto-arranging text in particular languages for easy readability.
Notepad… doesn’t do this. Instead, you’ll have to keep track of everything yourself, which can get to be a nightmare with anything but the simplest projects.
You’ll also wanna make sure whatever you’re using saves your stuff in a plain-text format. Rich-text editors like Microsoft Word don’t do this. As an example, here’s an example of my (normally very readable!) resume opened in a plain-text editor:
f7da b146 98c7 2221 7c15 595f 1fef afa6
d648 69c4 1344 05c7 91f5 8295 753b 7f73
b39d f18c 2db0 3479 2363 c1d5 8cc5 91b5
d63a 9dd9 b68a d798 2175 2d52 cc4d 7029
2443 dafc 942b 9b21 f994 a557 b160 29d2
6441 28d1 2fb6 e738 a1b5 b311 9195 493e
That’s the result of the plain-text editor opening it and assuming it’s, well, just text. Most of this comes from extra formatting stuff, like font faces, font weights, etc. that are not part of what the text itself ‘says’.
Which IDE you choose is generally up to you, but I personally love Sublime Text for its extensibility. Webstorm and Atom are also very popular, as is Visual Studio.
For Sublime Text, go here to download it and install. Got an IDE? Great. Now let’s quit all this dilly-dallying and get into some Node!
Installing Node
Installing Node’s pretty easy. Just go to NodeJS.org. You’ll see two green buttons. The left-hand one, the LTS version, is the stable version (and is the one you should use!). It’s been tried and tested, and is relatively stable. The other version is the ‘bleeding-edge’ build, and may not be 100% stable. Unless you absolutely know what you’re doing, it’s better to stick with the well-tested version. Take note of that little bit that mentions NPM too!
So go ahead and download and install that LTS version. I’ll wait. Got it? Let’s make sure. Open a terminal/command prompt and type node
. If everything worked out correctly, you should just be put on a blank new line. If you didn’t install it correctly, it’ll just tell you that it doesn’t know what ‘node’ is.
Congratulations! You’ve installed Node! Next, we’re gonna talk about Node ‘packages’, and how you organize a project in Node. Keep that command prompt/terminal open for now. As an aside, I’m gonna start just using the word ‘terminal’ for now, but if you’re on Windows, you’ll of course be using the command prompt.
Package.JSON: Node Packages and NPM
Node projects are organized by a special JSON file called ‘package.json’. This file contains info such as the project’s title, its author, and more importantly its dependencies (the stuff other people have written that you’re using in your project). So let’s not waste any more time and make our first NPM Project. Since this is eventually gonna be a server, we’ll call it myserver (names need to be all lowercase). For starters, we’ll create this package.json file using node’s package manager, NPM. If you’re wondering where you get NPM, don’t worry: it’s installed with Node.
Start by creating a new folder and cd
ing into it: mkdir myServer && cd myServer
. Then, make a package.json file by typing npm init
in your terminal. You’ll be walked through creating a package.json file (it does say that, and NodeJS likes to be nice and honest with you). Some specific suggestions:
- For version, I usually start out with 0.0.1 and follow Semantic Versioning, but that’s really up to you.
- For the description, go ahead and give it a nice helpful description. Something like “This is my first Node and Express server”.
- The ‘entry point’ is the file used if you decide to upload your package to npm so that others can use it. We’re not gonna do that, so you can just say “app.js” or something for now.
- The remaining items can just be left blank.
In your myServer
folder, you should now have a file labeled ‘package.json’. That’s your project file, and you can add packages to it using NPM. So let’s do that now, with chalk
, which is a nice little package for making colored text in the terminal (it’s great for specific error or status messages). To install chalk
, type the following:
npm install chalk --save
If you’re wondering (and you should be!), the --save
tells NPM ‘record that to the package.json file’. Normally, when someone sends you a node project, they’re not gonna send you all the dependencies explicitly with it. Instead, they’ll just send you a list of the dependencies, and say “make sure you download those too!”.
Go ahead and check your ‘package.json’. You should notice a new property, “dependencies”, with one entry: chalk
. If you’re wondering what the weird stuff after chalk
is, that’s just the version of that package NPM is using. We don’t really need chalk
for this project, so go ahead and run npm uninstall chalk
to remove that dependency.
Next, let’s install some actually helpful packages. Start with Express
, which is what we’ll use to write our routes. Remember, that’s npm install express --save
. We’re also gonna want nodemon
, which allows us to write a server that restarts whenever one of its files changes, and body-parser
, which makes sure we can parse objects within POST requests. Got all those? Great. As a final step, go into your package.json file, and find the scripts
object. Add the property "start"
, and give it the property of "nodemon app.js"
.
Next, we’ll talk about including modules within other modules: requiring!
What Dost Thou Require? require()
One of the great things about Node is the ability to include one file inside another by simply require()
ing that included file. Those of you coming from other languages like C, Python, or Java might be familiar with this with things like Python’s Import
. Those of you who are not coming from other languages can think of this like buying a brownie mix from the store and adding your own stuff, rather than making everthing from scratch. You’re require()
ing the brownie mix, which means you don’t have to ‘code’ that part yourself.
For starters, make a new file in your ‘myServer’ folder called ‘app.js’. Open that file in your favorite code editor. For starters, we’re gonna include the Express
module:
const express = require('express');
Again, that says “find the module named ‘express’, and include it”. Now we can use whatever functions and code Express has. Let’s add some more lines:
const http = require('http'),
app = express(),
routes = require('./routes'),
path = require('path'),
bodyParser = require('body-parser');
These lines are:
- http: Allows HTTP requests (GET, POST, PUT, DELETE) on the back-end. This will actually serve (heh) as our server for this project. You might also notice that we never did
npm install http --save
. That’s because the http
module is natively part of Node. We just gotta tell Node, yes, we do want that module.
- app: Running the express module as a function creates an instance of express (it’s a constructor function). This will be our server.
- routes: You can put all your routes (more on this in a bit!) in the
app.js
file. However, for sanity’s sake, you’ll probably wanna split them up into their own folder. Note the ‘./‘ here, which means we’re looking in a particular folder. In most cases, Node will assume that the first file in that folder called ‘index.js’ is the one you want. However, you could do ‘./routes/myRoutes.js’ instead. Go ahead and create a folder called ‘routes’, and put an index.js file in it.
- bodyParser: Note the name format here: JavaScript variables are not allowed to have dashes in the middle. Remember that this module allows us to read the contents of POST requests.
Next, we’ll talk about configuring your server!
Setting Things Up. app.use()
and middleware
Add the following lines to your app.js
file:
app.use(bodyParser.urlencoded({ extended: false })); //allows your app to accept UTF-8 encoded stuff.
app.use(bodyParser.json()); //allow us to parse JSON (so we cand send data across!)
app.set('view engine', 'html'); //what KIND of files will we be displaying on the front end?
app.set('views', path.join(__dirname, 'views')); //and where are those files?
//Next, let's tell Node where to find the so-called front-end files.
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'views')));
//Finally, let's tell node to include that `routes` folder.
app.use('/', routes);
And let’s go ahead and put some stuff in that index.js
file inside your routes
folder:
const express = require('express');
const router = express.Router();
const dog = {
breed:'Tyrannosaur',
name:'Fido'
};
//Routes will go on this line
module.exports=router;
Let’s take a look at what we just wrote. Firstly, we’re requiring Express
again. Next, we access Express
‘s Router()
method, which allows us to construct routes. Routes tell a server how to respond when it receives a particular URL. I’ll talk about this more in the next section.
Finally, we tell Node that, if any external modules request access to this file, we’re gonna wanna let them have access to the router
object. Note that, for example, the ‘dog’ object is not exported: if I require()
d this module, I would not have access to the dog object!
Now that we’ve got our app’s routes file set up, let’s actually put some routes in!
Routes: Where do We Go From Here?
As I’ve mentioned, routes basically tell our server how to respond when a particular URL is requested. It’s important to note that they do not just tell the server where to go; they tell it how to answer. This is essential to remember since the response may occasionally not be a file. For example, I might have a particular route at ‘mySite.com/serverTime’ that simply asks the Node process to tell me the current system time on the server.
With that clarification out of the way, let’s get to routing! Remove the line in your ‘./routes/index.js’ file that says //Routes will go on this line
, and replace it with the following:
router.get('/',function(req,res,next){
res.sendFile('index.html',{"root":'./views'});
});
Let’s break this down. Firstly, it’s method on the router object we brought in via const router = express.Router()
. It’s an HTTP GET method, which usually means it’s designed to, well, get information from the server while not giving a huge amount. Those of you familiar with the CRUD (Create, Read, Update, Delete) methodology of databases may note that this usually means it fits the ‘Read’ portion of that acronym. Next, we have the parameters that are sent to the .get()
method. The first is the URL that we want this route to respond to. In this case, it’s just the root route; it’ll be activated when we go to ‘mysite.com’ without any other…stuff.
Finally, there’s the callback. Router callbacks in Express
always have the same format:
- It’s a callback function, so the entire thing’s a function. That means the usual
function()
and {}
.
- It has three parameters:
req
: The Request object. In the case of GET requests, this can hold query string parameters as part of req.params
. In the case of POST requests, this holds the body of the post request in, well… req.body
.
res
: The Response object. A response isn’t technically required, but not saying anything is a bad idea: how else does the front end know its information has been recieved? Remember that, under normal circumstances, a webserver can only send a response to the front end when requested! So if you don’t use this to respond, you may not be able to send that information. Also, note that a response can only be sent once per request!
next
: Used for stuff like error-handling middleware. Often times, you’ll see something like if(errHappened) return next(err);
, which basically says “If there’s an error, go ahead and continue to the next route (that’s specifically designed to deal with errors)”.
- It uses that response object somehow. This can be done a number of ways:
res.send('Hiya!')
:Send a simple response. Note that because most responses are gonna be viewed in a web browser, you can send HTML and stuff like that across.
res.sendFile('myFile.html')
: This allows you to respond with a file (often HTML) when a particular route is used. For example, in the above route, you respond with the index.html
file. Note the object here and its ‘roots’ property, which basically tells us where the file can be found.
res.json({some:'jsonObject'})
: Send a specifically JSON response. Great if you’re requesting information of some sort.
res.end()
: End the request without sending data.
res.download('./files/file.mp3')
: Initiate a download (at the okay of the user, of course!). This includes a path to the file, and actually includes its own nested callback.
res.redirect('youreNotLoggedIn.html')
: Allows you to respond by redirecting the client. Useful if, for example, you have a login service. In the event that a check for a logged-in status fails, you may wanna use this to redirect the client to a ‘hey, you need to login to view this!’ page.
res.render('jediProfile.ejs',{name:'Luke Skywalker'})
: Used for so-called ‘template engines’ like EJS, Jade, etc. and can include stuff like template-specific variables. If you’re using HTML, don’t worry about this one.
res.status(418)
: Must be used with one of the other methods to actually respond, as all this does it determine the status code. Allows you to send a specific error code if an error occurs, rather than just 500 or 404 or something generic.
And yes, go ahead and create a ‘./views’ folder and put an index.html
file in there. It doesn’t matter what it has in it, as long as it’s in the right place.
Next, let’s add two more routes, just to give us a little variety. Put these after your first route.:
router.get('/err',function(req,res,next){
res.status(418).send('tea time!')
})
router.get('*',function(req,res,next){
res.send('<span style="background:#090; color:#fff">Oh no!</span> I dont have a route for that!');
});
The first route responds with a specific error code. Open up your browser console and you should see something like [HTTP/1.1 418 I'm a teapot 8ms]
. Yes, HTTP Error 418 is a real HTTP status code. The second route contains a wildcard, and basically means that if we go to any route, send this response.
You might ( == should) be wondering how the other routes get precedence over this ‘any’ route. When we go to ‘mysite.com/‘, for example, how does it know to trigger the index.html
route instead? Well, remember that JavaScript is single-threaded, and so things happen, generally, in a sequence. Since the ‘/‘ route comes first, it gets precedence.
Now, go ahead and try to start your server. Type npm start
in your terminal and press enter. You should see:
>nodemon app.js
[nodemon] 1.11.0
[nodemon] watching: *.*
[nodemon] starting `node app.js`, enter `rs`
[nodemon] clean exit - waiting for changes before restart
Well, it didn’t break, but clearly we’re missing something. That’s because while we’ve require()
d the http
module, we’ve yet to actually use it. Let’s do that now by passing our app into the http.Server()
method. At the bottom of your app.js
file, write const server = http.Server(app);
.
Still not working. We need one more line, so go ahead and tell your server to start listening. Write: server.listen(process.env.PORT || 8080);
And that’s it! Go to ‘localhost.com:8080’ in your browser, and see that your server now works! Try a bunch of routes, including the ones we implemented before.
If you’re wondering, the parameter passed to the .listen()
method basically says “Check our runtime environment for a port to run on, and if one exists, use it. Otherwise, use port 8080”. This is important to include (rather than simply doing server.listen(8080)
) as certain webhosts like Heroku want to specify their own ports (and will actually crash if you try to specify one for them!). This just saves some headache later.
Further Exploration
Adding More
Adding more stuff is simply a matter of adding more routes. Let’s try using some dynamic routes. Add the following to your ‘./routes/index.js’, before the wildcard (‘*’) route:
router.get('/sayHi/:myName',function(req,res,next){
res.send('Hello,'+req.params.myName+'! How are you today?')
});
Go to ‘localhost.com:8080/sayHi/Frodo’, and you’ll see it responds ‘Hello, Frodo! How are you today?’. GET parameters in Express are signified by a :
. In this case, we’re sending along the property myName
.
You can also send POST requests in a similar way. Assuming the route you send your post request to is ‘localhost.com:8080/sendInfo’, your route might look something like this:
router.post('/sendInfo',function(req,res,next){
//assume that the post object you sent has a 'name' property and an 'age' property.
if(req.body.age > 18){
res.send('Hi '+req.body.name+'. You are an adult!')
}else{
res.send('Hi '+req.body.name+'. You are NOT an adult!')
}
});
Front-End Files
One thing you may be wondering is where to put front-end JavaScript and CSS files. We’ve already discussed that HTML goes in the ‘./views’ folder, but what about making your website prettier (or more dynamic!)? Well, take a look back at the line in your ‘app.js’ that says app.use(express.static(path.join(__dirname, 'public')));
. Basically, that tells us to make the ‘./public’ folder available to the front end.
Go ahead and create that folder, and stick a file called ‘main.js’ in it. Then, in your ‘index.html’, insert the line <script src="./main.js"></script>
near the bottom. Note that since the ‘./public’ folder’s contents are now part of our front-end, we don’t need to include that as part of the path here: <script src="./public/main.js"></script>
would be wrong.
Finally, let’s actually put a script in their. For now, we’ll just tell our user when the page was loaded. In that ‘main.js’ file you just made, write document.body.append('This file was loaded at: '+ new Date());
. Reload the page (‘localhost.com:8080/‘), and you should notice it says when the page was loaded!
Conclusion
That’s it! Keep experimenting with routes, and have fun!