Hack School Session 4

Hello Hackers! :) We hope midterms are treating you guys well and welcome back to Hack School. Today we will be reviewing JavaScript and learning Express.js and routing. You are going to build a Hack School Library and the skills you learned in this session will also be used in BruinMessenger. After this lesson, you will be able to work with your team members to start on the BruinMessengers. October 26, 2016

Node.js and HTTP Review


As you may have guessed, we’re going to use Node.js to build a web server. To explain how that works, we can view Node.js as a bakery.


  • A client comes to the bakery with an order of, say, 10 brownies.
  • The cashier accepts the order and passes it on to the baker. The baker himself doesn’t know how to make brownies, but he knows how to follow the recipes to make brownies, so he gets the recipe. He checks his ingredients and tools to make sure they’re enough for the job.
  • He might decide to ask his assistants to fetch him some ingredients or wash dishes, or other long running tasks, and while he waits for them to finish, he’ll get started on the brownies.
  • When the assistants finish their tasks, the baker can pick up those tasks, and at the end of everything → fetching ingredients, mixing the batter, baking, and packaging, the baker gives the completed order to the cashier, who gives it to the client.


Now, if you know Node.js, you know I have described exactly what Node.js does:

  • The client is the web browser.
  • The cashier is the server.
  • The baker is the Node.js process.
  • The recipes are the scripts your write.
  • The ingredients are your static files, html, css, images, etc.
  • The tools are functions and libraries.

HTTP Requests and Responses

Remember when we talked about HTTP? Well here’s what an actual HTTP (Hypertext Transfer Protocol) request looks like. This is the format that clients and servers use to communicate with each other. There are two subtopics to cover: HTTP requests and HTTP responses.

Here is an example of a request. Each request has a method, resource, headers, and body.


The request tells the server what action the client wants to perform. In this case, it wants to GET an html page /doc/test.html. The next few lines are called headers. Headers are information about the request. Optionally, if you have a POST request, you can also send some data in the request. For example, when you log into a website, it’s a POST request.

An HTTP Response is similar.


Instead of a method, it has a status: 200 means OK. Other common ones are 403: Unauthorized and 500: Internal Server Error. You’re guaranteed to encounter these at some point in your lives. The response also contains headers relaying information about the response, and then the response itself, called the body. So remember, a request has a method, headers, and body. A response has a status, headers, and body.

HTTP Methods (Verbs)

Some of the different actions we can tell a server to do are GET, POST, PUT, DELETE. Each one of them is symbolic of something else. Use CRUD to remember it.

Remember CRUD: Create, Read, Update, Delete

POST: Create a resource

GET: Read a resource

PUT: Update a resource

DELETE: Delete a resource

The most common methods will be POST and GET.

Use POST to send data and GET to retrieve data.

We send a POST request if we want the server to CREATE a resource. GET to READ a resource. PUT to UPDATE a resource. DELETE to DELETE a resource. The most common ones are GET and POST. But remember, these are by convention. You should follow convention when writing your own server, but other servers may not necessarily follow convention, nor is it always appropriate.


What is Express? Express is pre-written JavaScript that we use to write web servers. In our analogy, Express is the baker and you write the recipes. We’re going to be working on a project called Hack School Library to familiarize you with Express.

Download the material here

Getting Started with Express

  1. Open the terminal

  2. cd to your project folder → starter-pack
  3. Type: npm init → follow the instructions
  4. Type: npm install express --save
  5. Type: npm install body-parser --save
  6. Type: npm install hbs --save
  7. Verify everything is ready: node verifyInstall.js
  8. Edit: index.js

Writing a Web Server — index.js

const express = require('express'); // import express
let app = express();	            // create the app

app.get('/', (req, res) => { // define route for '/'
    res.send('<h1>Hello, World!</h1>');

app.listen(3000);	// listen on port 3000

Hello, World!

  1. Go back to your terminal
  2. In the folder that contains your index.js type node index.js to start the server!
  3. If there were no errors, open a web browser and navigate to http://localhost:3000 and you should see this:


Request and Response Objects Express gives us easy-to-use functions and properties. These methods and properties are for reference, you don’t have to memorize them.

app.get('/', (req, res) => {
    // req is the 'request object' – information about the request
    // res is the 'response object' – set up and send the response
    req.params; // information passed in a GET request
    req.body; // information passed in a POST request
    res.send('string'); // send back an HTML string
    res.render('template file'); // render a template in views/
    res.redirect('file or route'); // redirect to a file or route

Well done! You are half way through! :)


What is Routing?

Routing is how we tell our server what to do for different parts of our website. Each part of our website is called a “route.”

Define a Route We can write the route directly:

app.get('/path/to/route', (req, res) => { /* handle */ });

or hand the task off to a router (behaves exactly the same as above):

let myRouter = express.Router();

myRouter.get('/route', (req, res) => { /* handle */ });
app.use('/path/to', myRouter); 

Special Arguments to Routes

  • Paths can be dynamic

  • Specified by :<variablename>' in the route

    • Put into a special object req.params
//   /greet/user/Nikhil ⇒ req.params.username === 'Nikhil'
//						⇒ would send back "<h1>Hello Nikhil!</h1>" to the browser
app.get('/greet/user/:username', (req, res) => {
    res.send("<h1>Hello " + req.params.username + "!</h1>");

Serving Static Files

Express can expose static HTML, CSS, JS, images by itself

  • Just tell it where to find them
// this goes before your app.get
// public is the folder you want to serve files from
// e.g.: public/css/style.css ⇒ http://localhost:3000/css/style.css

Exercise: Set up the Login Page

  1. Comment out all app.get directives

  2. Route '/' to redirect to login.html

  3. Restart the web server

    1. Must be done after every change to the JS
    2. Press ctrl+c in the terminal to quit the node process
    3. Run node index.js again to start the server.
  4. Now open http://localhost:3000

Exercise: Solution

app.get('/', (req, res) => {

HTML Forms and Express

Recall the basic HTML form:

<form name="myform" method="POST" action="/route">
    <input type="text" name="username" />
    <input type="submit" value="Save my username" />

Note that the form has a method. It can be GET or POST, and it must match whether we use app.get or app.post on the server side. The form also has an action. This denotes which route to send the request to on the server. In this example, we would need a route defined on the server that looks like this: app.post('/route', ...). Forms are used to send information. Each input in a form has a name. The value that is specified in the name field is the name of the property that we can access on our server to get the value passed for that input.

If we have this form, here is how we handle it. Note that we have to require('body-parser') and tell our app that we want to use it by writing app.use(bodyParser.urlencoded()). This lets us read HTML form data. Note: You may get a warning when you try to run your server. Ignore it.

const express = require('express');
const bodyParser = require('body-parser');

// … after app.use(express.static('public'));
app.use(bodyParser.urlencoded()); // enables req.body
app.post('/route', (req, res) => {
    console.log("I was sent", req.body.username);

Everytime the above form is submitted to the server, we would see a log statement in the command line which writes “I was sent “ followed by the data entered in the username input.

Exercise: Add and Handle the Login Form

  • Edit public/login.html

  • After the commented area, create a new form with:

    • method="POST" action="/login"
    • Input of type="text", with name="username"
    • Input of type="submit", with value="Enter Library"
  • Edit index.js

    • Add a route to handle a POST request to /login

      • Take the username you were sent and assign it to the global user variable
      • Send back a response saying "Hello <user>", where <user> is replaced with the username you were sent.
    • Add a route to handle a GET request to /logout

      • Set user to null and redirect to /

Exercise: Solution

<!-- public/login.html -->
<!-- TODO: Add the login form here -->
<form method="POST" action="/login">
	<input type="text" name="username" />
	<input type="submit" value="Enter Library" />
// index.js

app.post('/login', (req, res) => {
	user = req.body.username;
	res.send('Hello ' + user);

app.get('/logout', (req, res) => {
	user = null;


What is Handlebars?

  • Allows us to write in HTML, but refer to JS objects
  • Allows us to use res.render

We need to let Express know we want to use handlebars:

//… after app.use(bodyParser.urlencoded());
app.set("view engine", "hbs");

Here’s an example handlebars file. We technically write in HTML, but we can use the `` syntax to refer to objects that are passed to the render function in our Node.js.

<!-- views/about.hbs -->
<p id="name">My name is . I like:</p>

Here is how we would render it in our index.js:

app.get('/about', (req, res) => {
    res.render('about', {
    	name: "Nikhil Kansal",
    	things: ["HTML", "CSS", "JS"]

Express.js uses handlebars to render the file, replace the objects that are passed in, convert it to pure HTML, and send it back to the client. All in one step. Here is what would get sent back to the web browser in this example.

<p id="name">My name is Nikhil Kansal. I like:</p>

Creating the Book Library

Show the Book Library

Edit views/library.hbs

  1. After TODO 1, create a paragraph with id="welcome" and greet the user

  2. The JavaScript property name of the passed string is user
  3. In the same line, add a link to /logout

Edit index.js:

  1. Write a GET /library route. It should render the ‘library’ template with the object returned by makeLibraryPageState() if user !== null, and redirect to login.html otherwise
  2. Change the /login route to redirect to / instead of sending back "Hello <user>"
  3. Change the / route to redirect to login.html if user === null, otherwise redirect to /library.
  4. Add a callback as the second argument to app.listen that calls initBooks.



<!-- TODO 1: add the welcome message here -->
<p id="welcome">Welcome, <b></b>!</p> <a href="/logout">Logout</a>


app.get('/', (req, res) => {
	if (!user)
		return res.redirect('/login.html');
	return res.redirect('/library');

app.post('/login', (req, res) => {
	user = req.body.name || null;

app.get('/library', (req, res) => {
	if (!user)
		return res.redirect('/login.html');
	res.render('library', makeLibraryPageState());

app.listen(3000, () => {

Deleting Books

Edit index.js:

  • Write a GET /books/delete/:isbn route. It should:

  • Check if user === null → redirect to /library.
  • Find the book by the isbn passed into the route and get its index in the array.
  • If there is such a book, use books.splice(index, 1) to remove the book.
  • Redirect to /library



app.get('/books/delete/:isbn', (req, res) => {
	if (user) {
		let isbn = req.params.isbn || 0;
		for (let i = 0; i < books.length; i++) {
			if (books[i].isbn === isbn) {
				books.splice(i, 1);


Create the Search Form

Edit views/library.hbs

  1. After TODO 2, create a form with id="search" (stylistic purposes), method="POST", and action="/books/search"
  2. It should have one input with type="text", name="query", placeholder="Search..."

Edit index.js:

  1. Write a POST /books/search route. It should:

  2. Check if user === null → redirect to /library.
  3. Else, Render the ‘library’ template with the object returned by makeLibraryPageState({ books: filteredBooks })
  4. Where filteredBooks is the new array of books (objects) of all elements whose title, author, and/or isbn properties contain req.body.query
  5. Use String.indexOf and Array.filter (see MDN)



<!-- TODO 2: add the search form here -->
<form id="search" method="POST" action="/books/search" autocomplete="nope">
    <input type="text" name="query" placeholder="Search..."  value="" />


app.post('/books/search', (req, res) => {
    let query = req.body.query || "";
    if (query.length > 0 && user) {
        let filteredBooks = books.filter(book => {
            return book.title.indexOf(query)  !== -1 ||
                   book.author.indexOf(query) !== -1 ||
                   book.isbn.indexOf(query)   !== -1;
        res.render('library', makeLibraryPageState({
            books: filteredBooks,
            searchText: query
    } else {

Adding New Books

  • Take a look at the <form id="addbook"… block in views/library.hbs

  • Edit index.js:

    • Write a POST /books/add route. It should:

      • Check if  user === null→ redirect to /library.

      • Check the validity of what is being posted by the above form.

      • If everything is valid, add a new object to the array of books with the inputted title, author, isbn,and copies and redirect to /library

      • Otherwise, render the ‘library’ template with the returned object from the call:

        makeLibraryPageState({addFormBox: formBoxData }) where formBoxData is:

            message: true,
        	messageText: "An invalid input was entered",
        	messageSuccess: false,
        	formData: { title, author, isbn, copies }



app.post('/books/add', (req, res) => {
	if (!user)
		return res.redirect('/library');

	let title = req.body.title || "";
	let author = req.body.author || "";
	let isbn = req.body.isbn || "";
	let copies = parseInt(req.body.copies) || 0;
	if (title.length > 0 && author.length > 0 && isbn.length > 0 && copies > 0) {
		books.push({title, author, isbn, copies});	// add the book
	} else {
		res.render('library', makeLibraryPageState({
			addFormBox: {
				show: true,
				message: true,
				messageText: "An invalid argument was entered",
				messageSuccess: false,
				formInfo: {title, author, isbn, copies}


  • What if we want more than one user at once?
  • What if want to verify a user’s identity with a password?
  • Is an array really the best way to store all the books?
  • What if we have millions of books?


Let’s get started! Download and unzip this to your project folder:


  • Successfully load different pages of BruinMessenger:

  • Login
  • Rooms listing
  • 404 Not Found

Get Started

  1. Download our skeleton code

  2. See the TODOs in server.js

Congratulations! You are at the end of this post! Hopefully you read and understood everything. You are one step closer to success in life (or the messenger)! Now, find your teammates and start working on BruinMessenger! Good luck and survive midterms! :)

Written on November 5, 2016