diff --git a/chapter21/01_express_boilerplate_app.js b/chapter21/01_express_boilerplate_app.js
new file mode 100644
index 0000000..b119984
--- /dev/null
+++ b/chapter21/01_express_boilerplate_app.js
@@ -0,0 +1,36 @@
+/**
+ * Module dependencies.
+ */
+
+var express = require('express')
+ , routes = require('./routes');
+
+var app = module.exports = express.createServer();
+
+// Configuration
+
+app.configure(function(){
+ app.set('views', __dirname + '/views');
+ app.set('view engine', 'jade');
+ app.use(express.bodyParser());
+ app.use(express.methodOverride());
+ app.use(app.router);
+ app.use(express.static(__dirname + '/public'));
+});
+
+app.configure('development', function(){
+ app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
+});
+
+app.configure('production', function(){
+ app.use(express.errorHandler());
+});
+
+// Routes
+
+app.get('/', routes.index);
+
+app.listen(3000, function(){
+ console.log("Express server listening on port %d in %s mode", app.address().port,
+ app.settings.env);
+});
\ No newline at end of file
diff --git a/chapter21/02_new_version_routes.js b/chapter21/02_new_version_routes.js
new file mode 100644
index 0000000..f27cc06
--- /dev/null
+++ b/chapter21/02_new_version_routes.js
@@ -0,0 +1,9 @@
+/*
+ * GET home page.
+ */
+
+module.exports = function(app) {
+ app.get('/', function(req, res){
+ res.render('index', { title: 'Express' });
+ });
+};
\ No newline at end of file
diff --git a/chapter21/03_user_routes.js b/chapter21/03_user_routes.js
new file mode 100644
index 0000000..5ae498e
--- /dev/null
+++ b/chapter21/03_user_routes.js
@@ -0,0 +1,11 @@
+/*
+ * User Routes
+ */
+
+module.exports = function(app) {
+
+ app.get('/users/:name', function(req, res){
+ res.render('users/profile', {title: 'User profile'});
+ });
+
+};
\ No newline at end of file
diff --git a/chapter21/04_users_list.jade b/chapter21/04_users_list.jade
new file mode 100644
index 0000000..f5f8f51
--- /dev/null
+++ b/chapter21/04_users_list.jade
@@ -0,0 +1,3 @@
+h1 Users
+
+p User list should be here
\ No newline at end of file
diff --git a/chapter21/05_fake_users.json b/chapter21/05_fake_users.json
new file mode 100644
index 0000000..bd1fd4f
--- /dev/null
+++ b/chapter21/05_fake_users.json
@@ -0,0 +1,21 @@
+{
+
+ "frank": {
+ "username": "frank",
+ "name": "Frank Sinatra",
+ "bio": "Singer"
+ },
+
+ "jobim": {
+ "username": "jobim",
+ "name": "Antonio Carlos Jobim",
+ "bio": "Composer"
+ },
+
+ "fred": {
+ "username": "fred",
+ "name": "Fred Astaire",
+ "bio": "Dancer and Actor"
+ }
+
+}
\ No newline at end of file
diff --git a/chapter21/06_completed_user_route_listeners.js b/chapter21/06_completed_user_route_listeners.js
new file mode 100644
index 0000000..3853dc4
--- /dev/null
+++ b/chapter21/06_completed_user_route_listeners.js
@@ -0,0 +1,45 @@
+/*
+ * User Routes
+ */
+
+var users = require('../data/users');
+
+module.exports = function(app) {
+
+ app.get('/users', function(req, res){
+ res.render('users/index', {title: 'Users', users: users});
+ });
+
+ app.get('/users/new', function(req, res) {
+ res.render('users/new', {title: "New User"});
+ });
+
+ app.get('/users/:name', function(req, res, next){
+ var user = users[req.params.name];
+ if (user) {
+ res.render('users/profile', {title: 'User profile', user: user});
+ } else {
+ next();
+ }
+
+ });
+
+ app.post('/users', function(req, res) {
+ if (users[req.body.username]) {
+ res.send('Conflict', 409);
+ } else {
+ users[req.body.username] = req.body;
+ res.redirect('/users');
+ }
+ });
+
+ app.del('/users/:name', function(req, res, next) {
+ if (users[req.params.name]) {
+ delete users[req.params.name];
+ res.redirect('/users');
+ } else {
+ next();
+ }
+ });
+
+};
\ No newline at end of file
diff --git a/chapter21/07_user_list.jade b/chapter21/07_user_list.jade
new file mode 100644
index 0000000..19ee0be
--- /dev/null
+++ b/chapter21/07_user_list.jade
@@ -0,0 +1,10 @@
+h1 Users
+
+p
+ a(href="/users/new") Create new profile
+
+ul
+ - for(var username in users) {
+ li
+ a(href="/users/" + encodeURIComponent(username))= users[username].name
+ - };
\ No newline at end of file
diff --git a/chapter21/08_user_profile.jade b/chapter21/08_user_profile.jade
new file mode 100644
index 0000000..9c93369
--- /dev/null
+++ b/chapter21/08_user_profile.jade
@@ -0,0 +1,8 @@
+h1= user.name
+
+h2 Bio
+p= user.bio
+
+form(action="/users/" + encodeURIComponent(user.username), method="POST")
+ input(name="_method", type="hidden", value="DELETE")
+ input(type="submit", value="Delete")
\ No newline at end of file
diff --git a/chapter21/09_new_user_form.jade b/chapter21/09_new_user_form.jade
new file mode 100644
index 0000000..d0a0b6b
--- /dev/null
+++ b/chapter21/09_new_user_form.jade
@@ -0,0 +1,14 @@
+h1 New User
+
+form(method="POST", action="/users")
+ p
+ label(for="username") Username
+ input#username(name="username")
+ p
+ label(for="name") Name
+ input#name(name="name")
+ p
+ label(for="bio") Bio
+ textarea#bio(name="bio")
+ p
+ input(type="submit", value="Create")
\ No newline at end of file
diff --git a/chapter21/10_session_routes.js b/chapter21/10_session_routes.js
new file mode 100644
index 0000000..cc0d48a
--- /dev/null
+++ b/chapter21/10_session_routes.js
@@ -0,0 +1,34 @@
+/*
+ * Session Routes
+ */
+
+var users = require('../data/users');
+
+module.exports = function(app) {
+
+ app.dynamicHelpers({
+ session: function(req, res) {
+ return req.session;
+ }
+ });
+
+ app.get('/session/new', function(req, res) {
+ res.render('session/new', {title: "Log in"});
+ });
+
+ app.post('/session', function(req, res) {
+ if (users[req.body.username] &&
+ users[req.body.username].password === req.body.password) {
+ req.session.user = users[req.body.username];
+ res.redirect('/users');
+ } else {
+ res.redirect('/session/new')
+ }
+ });
+
+ app.del('/session', function(req, res, next) {
+ req.session.destroy();
+ res.redirect('/users');
+ });
+
+};
\ No newline at end of file
diff --git a/chapter21/11_header_user_partial.jade b/chapter21/11_header_user_partial.jade
new file mode 100644
index 0000000..a24c79f
--- /dev/null
+++ b/chapter21/11_header_user_partial.jade
@@ -0,0 +1,19 @@
+- if (session.user) {
+
+ p
+ span Hello
+ span= session.user.name
+ span !
+ p
+ form(method="POST", action="/session")
+ input(type="hidden", name="_method", value="DELETE")
+ input(type="submit", value="Log out")
+
+- } else {
+
+ p
+ a(href="/session/new") Login
+ span or
+ a(href="/users/new") Register
+
+- }
\ No newline at end of file
diff --git a/chapter21/12_changed_layout_file.jade b/chapter21/12_changed_layout_file.jade
new file mode 100644
index 0000000..d02a1b9
--- /dev/null
+++ b/chapter21/12_changed_layout_file.jade
@@ -0,0 +1,8 @@
+!!!
+html
+ head
+ title= title
+ link(rel='stylesheet', href='/stylesheets/style.css')
+ body
+ header!= partial('session/user')
+ section#main!= body
\ No newline at end of file
diff --git a/chapter21/13_login_form.jade b/chapter21/13_login_form.jade
new file mode 100644
index 0000000..c01197c
--- /dev/null
+++ b/chapter21/13_login_form.jade
@@ -0,0 +1,11 @@
+h1 Log in
+
+form(method="POST", action="/session")
+ p
+ label(for="username") User name:
+ input#username(name="username")
+ p
+ label(for="password") Password:
+ input#password(type="password", name="password")
+ p
+ input(type="submit", value="Log in");
\ No newline at end of file
diff --git a/chapter21/14_user_signup_new_version.jade b/chapter21/14_user_signup_new_version.jade
new file mode 100644
index 0000000..03f4591
--- /dev/null
+++ b/chapter21/14_user_signup_new_version.jade
@@ -0,0 +1,17 @@
+h1 New User
+
+form(method="POST", action="/users")
+ p
+ label(for="username") Username
+ input#username(name="username")
+ p
+ label(for="name") Name
+ input#name(name="name")
+ p
+ label(for="password") Password
+ input#password(type="password", name="password")
+ p
+ label(for="bio") Bio
+ textarea#bio(name="bio")
+ p
+ input(type="submit", value="Create")
\ No newline at end of file
diff --git a/chapter21/15_not_logged_in_middleware.js b/chapter21/15_not_logged_in_middleware.js
new file mode 100644
index 0000000..e6c82e1
--- /dev/null
+++ b/chapter21/15_not_logged_in_middleware.js
@@ -0,0 +1,9 @@
+function notLoggedIn(req, res, next) {
+ if (req.session.user) {
+ res.send('Unauthorized', 401);
+ } else {
+ next();
+ }
+}
+
+module.exports = notLoggedIn;
\ No newline at end of file
diff --git a/chapter21/16_session_routes_new_version.js b/chapter21/16_session_routes_new_version.js
new file mode 100644
index 0000000..7554f7a
--- /dev/null
+++ b/chapter21/16_session_routes_new_version.js
@@ -0,0 +1,35 @@
+/*
+ * Session Routes
+ */
+
+var users = require('../data/users');
+var notLoggedIn = require('./middleware/not_logged_in');
+
+module.exports = function(app) {
+
+ app.dynamicHelpers({
+ session: function(req, res) {
+ return req.session;
+ }
+ });
+
+ app.get('/session/new', notLoggedIn, function(req, res) {
+ res.render('session/new', {title: "Log in"});
+ });
+
+ app.post('/session', notLoggedIn, function(req, res) {
+ if (users[req.body.username] &&
+ users[req.body.username].password === req.body.password) {
+ req.session.user = users[req.body.username];
+ res.redirect('/users');
+ } else {
+ res.redirect('/session/new');
+ }
+ });
+
+ app.del('/session', function(req, res, next) {
+ req.session.destroy();
+ res.redirect('/users');
+ });
+
+};
\ No newline at end of file
diff --git a/chapter21/17_user_routes_new_version.js b/chapter21/17_user_routes_new_version.js
new file mode 100644
index 0000000..f5cacc2
--- /dev/null
+++ b/chapter21/17_user_routes_new_version.js
@@ -0,0 +1,45 @@
+/*
+ * User Routes
+ */
+
+var users = require('../data/users');
+var notLoggedIn = require('./middleware/not_logged_in');
+
+module.exports = function(app) {
+
+ app.get('/users', function(req, res){
+ res.render('users/index', {title: 'Users', users: users});
+ });
+
+ app.get('/users/new', notLoggedIn, function(req, res) {
+ res.render('users/new', {title: "New User"});
+ });
+
+ app.get('/users/:name', function(req, res, next){
+ var user = users[req.params.name];
+ if (user) {
+ res.render('users/profile', {title: 'User profile', user: user});
+ } else {
+ next();
+ }
+ });
+
+ app.post('/users', notLoggedIn, function(req, res) {
+ if (users[req.body.username]) {
+ res.send('Conflict', 409);
+ } else {
+ users[req.body.username] = req.body;
+ res.redirect('/users');
+ }
+ });
+
+ app.del('/users/:name', function(req, res, next) {
+ if (users[req.params.name]) {
+ delete users[req.params.name];
+ res.redirect('/users');
+ } else {
+ next();
+ }
+ });
+
+};
\ No newline at end of file
diff --git a/chapter21/18_load_user_middleware.js b/chapter21/18_load_user_middleware.js
new file mode 100644
index 0000000..91c73a3
--- /dev/null
+++ b/chapter21/18_load_user_middleware.js
@@ -0,0 +1,12 @@
+var users = require('../../data/users');
+
+function loadUser(req, res, next) {
+ var user = req.user = users[req.params.name];
+ if (! user) {
+ res.send('Not found', 404);
+ } else {
+ next();
+ }
+}
+
+module.exports = loadUser;
\ No newline at end of file
diff --git a/chapter21/19_user_routes_new_version.js b/chapter21/19_user_routes_new_version.js
new file mode 100644
index 0000000..17c6f64
--- /dev/null
+++ b/chapter21/19_user_routes_new_version.js
@@ -0,0 +1,37 @@
+/*
+ * User Routes
+ */
+
+var users = require('../data/users');
+var notLoggedIn = require('./middleware/not_logged_in');
+var loadUser = require('./middleware/load_user');
+
+module.exports = function(app) {
+
+ app.get('/users', function(req, res){
+ res.render('users/index', {title: 'Users', users: users});
+ });
+
+ app.get('/users/new', notLoggedIn, function(req, res) {
+ res.render('users/new', {title: "New User"});
+ });
+
+ app.get('/users/:name', loadUser, function(req, res, next){
+ res.render('users/profile', {title: 'User profile', user: req.user});
+ });
+
+ app.post('/users', notLoggedIn, function(req, res) {
+ if (users[req.body.username]) {
+ res.send('Conflict', 409);
+ } else {
+ users[req.body.username] = req.body;
+ res.redirect('/users');
+ }
+ });
+
+ app.del('/users/:name', loadUser, function(req, res, next) {
+ delete users[req.user.username];
+ res.redirect('/users');
+ });
+
+};
\ No newline at end of file
diff --git a/chapter21/20_restrict_user_to_self_middleware.js b/chapter21/20_restrict_user_to_self_middleware.js
new file mode 100644
index 0000000..566ebf2
--- /dev/null
+++ b/chapter21/20_restrict_user_to_self_middleware.js
@@ -0,0 +1,9 @@
+function restrictUserToSelf(req, res, next) {
+ if (! req.session.user || req.session.user.username !== req.user.username) {
+ res.send('Unauthorized', 401);
+ } else {
+ next();
+ }
+}
+
+module.exports = restrictUserToSelf;
\ No newline at end of file