In this tutorial we’ll explore the Express NodeJS framework. It provides all of the capabilities of NodeJS, also makes it easy to build robust web applications quite easily using a MVC format. I’ll cover how to install on Windows, Mac and Linux. We’ll then explore building the server, routes, incorporating Handlebars, incorporating Bootstrap, ports, middleware, app.use, app.get, catching / throwing errors, handling forms, file uploads, cookies, sessions, reading / writing files and much more.
This will serve as an overview of the whole Express framework, while the next tutorial will how to incorporate a MongoDB database into an Express app. All of the code is down below.
If you like videos like this, it helps me if you tell Google with a click here [googleplusone]
The code and images are here for download
Code From the Video
expresstut.js
// We will structure here a series of possible responses
// depending on the URL requested. As we travel through
// the pipeline we will perform needed actions using middleware
// functions. If a response is valid we will display the
// correct view and if not we will handle errors.
// This is our projects entry point. If you start the
// server by typing node expresstut.js and then open the
// browser at loclhost:3000 you'll get a 404 error if
// you haven't defined any routes
// Import the express module
var express = require('express');
var app = express();
// Block the header from containing information
// about the server
app.disable('x-powered-by');
// Set up Handlebars
// Create a directory named views and then another named layouts
// in it
// Define main.handlebars as the default layout
// Create these files in the views directory and define the
// HTML in them home.handlebars, about.handlebars,
// 404.handlebars and 500.handlebars
var handlebars = require('express-handlebars').create({defaultLayout:'main'});
app.engine('handlebars', handlebars.engine);
app.set('view engine', 'handlebars');
// Required when using POST to parse encoded data
// npm install --save body-parser
app.use(require('body-parser').urlencoded({extended: true}));
// Formidable is required to accept file uploads
// npm install --save formidable
var formidable = require('formidable');
// Import credentials which are used for secure cookies
// Install the cookie middleware
// npm install --save cookie-parser
var credentials = require('./credentials.js');
app.use(require('cookie-parser')(credentials.cookieSecret));
// Defines the port to run on
app.set('port', process.env.PORT || 3000);
// Create a directory called public and then a directory
// named img inside of it and put your logo in there
app.use(express.static(__dirname + '/public'));
// Define some routes. app.get receives a path and a
// function and it defines our routes. The path isn't
// case sensitive and doesn't care about trailing path
// information.
// The req object represents the HTTP request and
// contains the query string, parameters, body, header
// The res object is the response Express sends
// when it receives a request
app.get('/', function(req, res){
// Point at the home.handlebars view
res.render('home');
});
// This is an example of middleware It receives a request
// object, response object and the next function
// As we look for the correct information to serve it executes
// and then next() says to continue down the pipeline
app.use(function(req, res, next){
console.log('Looking for URL : ' + req.url);
next();
});
// You can also report and throw errors
app.get('/junk', function(req, res, next){
console.log('Tried to access /junk');
throw new Error('/junk does\'t exist');
});
// Catches the error and logs it and then continues
// down the pipeline
app.use(function(err, req, res, next){
console.log('Error : ' + err.message);
next();
});
// If we want /about/contact we'd have to define it
// before this route
app.get('/about', function(req, res){
// Point at the about.handlebars view
// Allow for the test specified in tests-about.js
res.render('about');
});
// Link to contact view
app.get('/contact', function(req, res){
// CSRF tokens are generated in cookie and form data and
// then they are verified when the user posts
res.render('contact', { csrf: 'CSRF token here' });
});
// Sent here after the form is processed
app.get('/thankyou', function(req, res){
res.render('thankyou');
});
// Receive the contact form data and then redirect to
// thankyou.handlebars
// contact.handlebars calls process to process the form
app.post('/process', function(req, res){
console.log('Form : ' + req.query.form);
console.log('CSRF token : ' + req.body._csrf);
console.log('Email : ' + req.body.email);
console.log('Question : ' + req.body.ques);
res.redirect(303, '/thankyou');
});
// Open file-upload.handlebars and store the current year
// and month. When the form is submitted the year and month
// will be passed
app.get('/file-upload', function(req, res){
var now = new Date();
res.render('file-upload',{
year: now.getFullYear(),
month: now.getMonth() });
});
// file-upload.handlebars contains the form that calls here
app.post('/file-upload/:year/:month',
function(req, res){
// Parse a file that was uploaded
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, file){
if(err)
return res.redirect(303, '/error');
console.log('Received File');
// Output file information
console.log(file);
res.redirect( 303, '/thankyou');
});
});
// Demonstrate how to set a cookie
app.get('/cookie', function(req, res){
// Set the key and value as well as expiration
res.cookie('username', 'DerekBanas', {expire : new Date() + 9999}).send('username has the value of : DerekBanas');
});
// Show stored cookies in the console
app.get('/listcookies', function(req, res){
console.log("Cookies : ", req.cookies);
res.send('Look in console for cookies');
});
// Delete a cookie
app.get('/deletecookie', function(req, res){
res.clearCookie('username');
res.send('username Cookie Deleted');
});
// Storing session information can be done in a few ways.
// For development we can work with a memory store
// Stores the session id in a cookie and the session data
// on the server
// npm install --save express-session
var session = require('express-session');
// parseurl provides info on the url of a request object
// npm install --save parseurl
var parseurl = require('parseurl');
app.use(session({
// Only save back to the session store if a change was made
resave: false,
// Doesn't store data if a session is new and hasn't been
// modified
saveUninitialized: true,
// The secret string used to sign the session id cookie
secret: credentials.cookieSecret,
}));
// This is another example of middleware.
app.use(function(req, res, next){
var views = req.session.views;
// If no views initialize an empty array
if(!views){
views = req.session.views = {};
}
// Get the current path
var pathname = parseurl(req).pathname;
// Increment the value in the array using the path as the key
views[pathname] = (views[pathname] || 0) + 1;
next();
});
// When this page is accessed get the correct value from
// the views array
app.get('/viewcount', function(req, res, next){
res.send('You viewed this page ' + req.session.views['/viewcount'] + ' times ');
});
// Reading and writing to the file system
// Import the File System module : npm install --save fs
var fs = require("fs");
app.get('/readfile', function(req, res, next){
// Read the file provided and either return the contents
// in data or an err
fs.readFile('./public/randomfile.txt', function (err, data) {
if (err) {
return console.error(err);
}
res.send("The File : " + data.toString());
});
});
// This writes and then reads from a file
app.get('/writefile', function(req, res, next){
// If the file doesn't exist it is created and then you add
// the text provided in the 2nd parameter
fs.writeFile('./public/randomfile2.txt',
'More random text', function (err) {
if (err) {
return console.error(err);
}
});
// Read the file like before
fs.readFile('./public/randomfile2.txt', function (err, data) {
if (err) {
return console.error(err);
}
res.send("The File : " + data.toString());
});
});
// Defines a custom 404 Page and we use app.use because
// the request didn't match a route (Must follow the routes)
app.use(function(req, res) {
// Define the content type
res.type('text/html');
// The default status is 200
res.status(404);
// Point at the 404.handlebars view
res.render('404');
});
// Custom 500 Page
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500);
// Point at the 500.handlebars view
res.render('500');
});
app.listen(app.get('port'), function(){
console.log('Express started on http://localhost:' +
app.get('port') + '; press Ctrl-C to terminate');
});
main.handlebars
<!DOCTYPE html>
<html lang="en">
<head>
<!-- References a Handlebars partial in views/partials/head.handlebars -->
{{>head}}
<title>Express Tutorial</title>
</head>
<body>
<!-- References a Handlebars partial in views/partials/bsnavbar.handlebars -->
{{>bsnavbar}}
<!-- The HTML for each view goes here -->
<div class="container">
{{{body}}}
</div>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
</body>
</html>
.gitignore
# Run npm init creates package.json which defines the # entry point as expresstut.js # Install Express by typing npm install --save express # in the terminal # Install the Express Handlebars package # npm install --save express-handlebars #ignore packages installed by npm node_modules *~ .DS_Store credentials.js # Create a Git repository with git init # Add files with 'git add -A' in the terminal # Commit the changes with 'git commit -m "Initial Commit"'
credentials.js
// Used to encrypt cookies. Use a random string
// Add this to .gitignore
module.exports = { cookieSecret: 'force ability health couple', };
README.md
Express Tutorial : Describe Project Here ======= Sub-heading ----------- ### Another deeper heading Markdown is plain text formatting designed to convert to HTML Paragraphs are separated by a blank line. Leave 2 spaces at the end of a line to do a line break Text attributes *italic*, **bold**, `monospace`, ~~strikethrough~~ . A [link](http://example.com). Unordered list: * apples * oranges * pears Numbered list: 1. apples 2. oranges 3. pears
bsnavbar.handlebars
<div class="container">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="pull-left" href="#"><img src="/img/NTTLogosm.png"></a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="/">Home <span class="sr-only">(current)</span></a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
<li><a href="/file-upload">File Upload</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
</div>
head.handlebars
<meta charset="UTF-8"> <!-- If IE use the latest rendering engine --> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Set the page to the width of the device and set the zoom level --> <meta name="viewport" content="width = device-width, initial-scale = 1"> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
The Views
------ 404.handlebars ------
<h3>404 : Not Found</h3>
------ 500.handlebars ------
<h3>500 : Server Error</h3>
------ about.handlebars ------
<div class="page-header">
<h3>About Us</h3>
</div>
------ contact.handlebars ------
<div class="page-header">
<h3>Contact Us</h3>
</div>
<div class="contactUs">
<h4>Send Us your Questions</h4>
<form class="formContactUs" role="form"
action="/process?form=contactus" method="POST">
<input type="hidden" name="_csrf" value="{{csrf}}">
<div class="form-group">
<label for="fieldEmail" class="col-sm-2 control-label">
Email</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="fieldEmail"
name="email">
</div>
</div> <!-- End of form-group -->
<div class="form-group">
<label for="fieldQues" class="col-sm-2 control-label">
Question</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="fieldQues"
name="ques">
</div>
</div> <!-- End of form-group -->
<div class="form-group">
<div class="col-sm-4">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</div> <!-- End of form-group -->
</form>
</div> <!-- End of contactUs -->
------ error.handlebars ------
<div class="page-header">
<h3>Something Went Wrong</h3>
</div>
------ file-upload.handlebars ------
<form class="form-horizontal" role="form"
enctype="multipart/form-data" method="POST"
action="/file-upload/{year}/{month}">
<div class="form-group">
<label for="fieldName" class="col-sm-2 control-label"> Name
</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="fieldName"
name="name">
</div>
</div>
<div class="form-group">
<label for="fieldEmail" class="col-sm-2 control-label"> Email
</label>
<div class="col-sm-4">
<input type="email" class="form-control"
required id="fieldEmail" name="email">
</div>
</div>
<div class="form-group">
<label for="fieldPhoto" class="col-sm-2 control-label">
Image</label>
<div class="col-sm-4">
<!-- Only accept images -->
<input type="file" class="form-control" required
accept="image/*" id="fieldPhoto" name="photo">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-4">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
------ home.handlebars ------
<div class="page-header">
<h3>Welcome to my Express Tutorial</h3>
</div>
------ thankyou.handlebars ------
<div class="page-header">
<h3>Thank you for your Submission</h3>
</div>