Express

CSCI-UA.0480-008

So… We Learned About Node's HTTP Module

It was little bit low level. What were some objects that we looked at?

Creating a Web Server

We brought up a web server and served some simple static pages by…

  1. creating a Server object that listens on a particular port
  2. specifying a function to be called when a request event is triggered
  3. using the passed in request and response objects in the callback to create a response


Setting Up

To create a Server object that

  • listens on port 3000
  • has callback, a request handler, bound to a request event


We needed to do two things


// bring in the http module
const http = require('http')

// create a server object that listens on port 3000
// ...and bind the handleRequest function to a requeset event
http.createServer(handleRequest).listen(3000)

Handling Requests

We created a callback function to handle requests. What were the two arguments that it could take?

  • a Request object (we named this req)
  • a Response object (we named this res)



function handleRequest(req, res) { ... } 

The Request (IncomingMessage) Object

What did we use the IncomingMessage (request) object for?

We mainly just used it to determine what url the client was attempting to request:

if(req.url == '/') ..

The Response Object

What did we use the Response object for?

We used it to send back the response headers and body. What were two methods that we used on it?

  • writeHead(status, headers)
  • end(body)

res.writehead(200, {'content-type':'text/plain'});
res.end('hello');

It Looks a Little Something Like This:

So, all together


const http = require('http');

http.createServer(handleRequest).listen(3000);
console.log('starting server on port 3000';

function handleRequest(req, res) {
	if(req.url == '/') {
		res.writehead(200, {'content-type':'text/plain'});
		res.end('hello');
	} else {
		res.writeHead(404, {'Content-Type':'text/plain'});
		res.end('Not Found');

	}
}

Serving Static Files

We modified our program so that it served static files. How did we do that (what module and method did we use?)

We used the fs module.

…and we used the readFile method, which takes a path and a callback.


// within our serveStatic function
fs.readFile(path, function(err, data) { ... });

serveStatic

Our serveStatic function did two things:

  1. attempted to read the contents of a file…
  2. and when it finished, it would send back an HTTP response

function serveStatic(res, path, contentType, resCode) {
	fs.readFile(path, function(err, data) {
		if (err) {
			res.writeHead(500, { 'Content-Type': 'text/plain' }); 
			res.end('500 - Internal Error');
		} else {
			res.writeHead(resCode, { 'Content-Type': contentType }); 
			res.end(data);
		}
	});
}

Using serveStatic

We modified our request handling callback to use our new serveStatic function instead of sending back a response directly.


function handleRequest(req, res) {
	if (req.url === '/') {
		serveStatic(res, './public/index.html', 'text/html', 200);
	} else if (req.url === '/about') {
		serveStatic(res, './public/about.html', 'text/html', 200);
	}
	// remainder of function definition omitted for brevity
}

That Wasn't So Bad

(At least it was better than the net module, right?)

Some Shortcomings

Well… maybe some if it was not so great. What were some challenges in writing that program, and what were some missing features?

  • the URLs are pretty brittle; they don't handle trailing slashes, query strings, etc. … without a lot of work
  • for every response…
    • we have to set the status code
    • as well as the content-type headers
    • a bit of a pain (accommodating all of the possible assets that could served, such as images, css, video, etc.)
  • the files are read from the disk every time (no caching)
  • there's a lot of manual work in general

A Little Help

Let's use a server side web framework. A web framework is a set of tools, libraries or software that reduces the overhead of developing web applications.

Some features that a web framework may provide are:

  • templating - to keep logic out of your presentation
  • routing - for mapping urls to pages/functionality
  • middleware - a pipeline of functions to manipulate and work with the request and response
  • database access - an abstraction layer for dealing with databases
  • general project structure - a standard way for organizing your project

Web Frameworks

Web frameworks can be very featureful:

  • providing everything for you from database access to templating…
  • and even dictating your project layout
  • (these tend to be larger, and occasionally more complex)


Web frameworks can also be very minimal:

  • only providing a small amount of core functionality
  • … and leaving other features to be integrated piecemeal as needed
  • (these tend to be smaller, and more bare bones)


Web frameworks can generally be categorized in this manner, or fall somewhere inbetween.

There are a lot of options for web frameworks, and they vary by language:

  • ruby - rails, sinatra
  • python - django, flask
  • PHP - laravel, symfony, slim
  • node - express


The highlighted ones are microframeworks.

Microframeworks

Generally, a microframework, or a minimal web framework:

  • has a simple, but extensible set of core functionality
  • won't make too many design decisions for you (and the ones that are made are usually changeable)
  • leaves many features up to the developer (and usually relies on third-party tools and libraries for those features)
  • maybe even fits in a single file!

We're Using Express, a "minimal and flexible Node.js web application framework"

Express

We'll be using Express 4. A little bit about Express…

  • built on top of node and node's http module
  • you'll find some familiar (but augmented) objects, like request and response
  • minimal, more like sinatra or flask rather than rails or django
  • flexible - your project layout is up to you, you choose what features you'd like integrated, etc.

Express 4 Features

Some features that Express comes with:

  • extends request and response objects
    • response.sendFile
    • response.redirect
    • request.ip
    • etc.
  • routing
  • views and templating
  • middleware
  • scaffolding

Some things that Express doesn't do (on its own):

  • database access (we'll have to bring in another module for that)
  • no opinion on how your project is structured - it's up to you to make it as organized or unorganized as you want!

When to Use Express

Some use cases for express

  • an API
  • an API backed single page web app
  • a traditional html page based app
  • a hybrid single page web app / traditional app

Installing Express

How do we install express?


npm install express

What if we want to save it as a dependency of our project?


// if you don't already have a package.json
npm init

// install express and save dependency to package.json
npm install express --save

A Reminder, We're Using Express 4

A Simple Express Program

Hello World


const express = require('express');
const app = express();

app.get('/', function(req, res){
	res.send('hello');
});

app.listen(3000);
console.log('Started server on port 3000');

Another URL

Let's try adding another url. The URL should be /faq.


app.get('/faq', function(req, res) {
	res.send('you has q, i has answer');
});

Testing it Out…

Try navigating to your app with…

  • http://localhost:3000/
  • http://localhost:3000/faq
  • http://localhost:3000/faq?question=1
  • http://localhost:3000/faq/
  • http://localhost:3000/faQ
  • http://localhost:3000/nope

What are some differences with our previous implementation using only node's http module?

  • trailing slashes and query strings work
  • case insensitive
  • content type is set implicitly to text/html
  • 404's built in

Line by Line


// require the express module
const express = require('express');

// create our express app
const app = express();

// use a router to bind a callback, a request handler
// to a particular url
app.get('/', function(req, res){

	// sends back a response; that is all
	res.send('hello');
});

// listen on port 3000
app.listen(3000);
console.log('Started server on port 3000');

express() and VERB()

express() - creates a new express application

  • allows for configuration
  • takes on functionality of Server object in http module only example
  • note that you can call some methods on it based on http verbs (request methods)


app.VERB(path, [callback]…, callback) - defines routes

  • verb is an HTTP request method (such as get)
  • maps a url to a callback (or multiple callback functions)
  • path is case insensitive, can have trailing slash and query string

send() and set()

res.send([body]) - send a response

  • sends all set headers
  • if the body is a string, content type is set to text/html
  • if the body is an object or an array, content type is set to application/json


Of course, you can still manipulate headers before sending…

res.set() - set a response header

Serving Static Files

This code will allow any file found in the public directory in your project to be served as a static file!


const path = require("path");

const publicPath = path.resolve(__dirname, "public");
app.use(express.static(publicPath));

No more individual urls (YES!)

Path and Static Middleware

Use the path module to create a path that specifies where your static files are located.


// bring in the path module
const path = require("path");

// create a cross-platform compatible path name (don't just use public)
const publicPath = path.resolve(__dirname, "public");

Use the built-in static files middleware to serve up any file in publicPath


app.use(express.static(publicPath));

Let's Try Serving Some Static Files

Remember to actually create the public directory

Generating / Rendering HTML

Serving flat HTML files is nice and all, but is it adequate for building all sorts of web applications? →

Well, we want to serve dynamic content. That is, our web application will be generating html on-the-fly.

To do this, we'll need a way to:

  1. create templates that content can be dropped into
  2. …and render those templates into an html document (or whatever format your application requires)

Templating

There are many templating solutions that we can use, both on the server side and the client side.

  • jade/pug comes with express, and it has a meta language for writing html!
    • terse syntax based on indentation (no closing tags!)
    • very quick to write, but you have to learn a lot more new syntax
  • handlebars is based off of a basic templating language called mustache
    • it's basically just html
    • with some special tokens for inserting data

We'll be using handlebars.

(slightly less to learn … but definitely feel free to use jade/pug instead)

Handlebars

First, install the express handlebars module:


npm install hbs --save


And in your code, bring in handlebars for templating:


app.set('view engine', 'hbs');


Render a template!


res.render('index', { "greeting":"HELLLOOOO" });

Layouts

In views/layout.hbs

(Notice 3 curly braces!)



{{{ body }}}

Templates

In views/viewname.hbs … drop in your content


{{ greeting }} world!