Handling AJAX Calls With Node.js and Express (Part 3)

Here is an index of all the articles in the series that have been published to date:

Right now we have a working application, with simple functionality: enter a search keyword, scrape Craigslist, append search results to the DOM via Handlebars:

main

Let’s pause for a minute and think about the end goal of this application. We want users to be able to search, save, and apply for jobs. We’ll discuss this in greater detail in the next post, but for now, let’s go ahead and add user authentication via Passport as well as MongoDB.


Setup

Open your terminal, navigate to your project’s root directory, and then install the following packages:

1
2
3
4
$ npm install passport --save
$ npm install passport-google --save
$ npm install mongodb --save
$ npm install mongoose --save

Once installed, require the dependencies in “app.js”:

1
2
3
var mongoose = require('mongoose')
var passport = require('passport')
var GoogleStrategy = require('passport-google').Strategy;

Finally, open a new terminal window, install mongoDB globally, then run the mongo daemon:

1
2
$ npm install mongodb
$ mongod

Update app.js

Add the following code, just below the development config section:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// serialize and deserialize
passport.serializeUser(function(user, done) {
  done(null, user);
});
passport.deserializeUser(function(obj, done) {
  done(null, obj);
});

// config
passport.use(new GoogleStrategy({
  returnURL: 'http://127.0.0.1:3000/auth/google/callback',
  realm: 'http://127.0.0.1:3000'
},
function(identifier, profile, done) {
  process.nextTick(function () {
    profile.identifier = identifier;
    return done(null, profile);
  });
 }
));

// test authentication
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) { return next(); }
  res.redirect('/')
}

Here we are handling the login and authentication via Google.

Next, update the routes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// routes
app.get('/', function(req, res){
  res.render('index', { user: req.user });
});

app.get('/search', ensureAuthenticated, function(req, res){
  res.render('search', { user: req.user });
});

app.get('/searching', function(req, res){
  // input value from search
  var val = req.query.search;
  // url used to search yql
  var url = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20craigslist.search" +
  "%20where%20location%3D%22sfbay%22%20and%20type%3D%22jjj%22%20and%20query%3D%22" + val + "%22&format=" +
  "json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";

  requests(url,function(data){
      res.send(data);
  });
});

app.get('/auth/google',
  passport.authenticate('google'),
  function(req, res){
});
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/' }),
  function(req, res) {
    res.redirect('/search');
  });

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

Go through this code slowly to make sure you understand at a high-level what’s going on. Comment if you have questions.

Then update the middleware to handle sessions and passport initialization:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'my_precious' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

Update the Jade files

Since we now have several more routes, let’s get our views straightened out.

First, rename “index.jade” to “search.jade” since the searching actually happens on a different route. Update the code to include a logout option:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extends layout

block content
  h1 search sf jobs
  a(href='/logout') Logout
  br
  br
  input#search(type="search", placeholder="Search Craig's Jobs")
  ul#results
  include template.html

script(src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js")   
script(src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.min.js")
script(src="/javascripts/main.js")

Next, go ahead and add a new “index.jade” file:

1
2
3
4
5
6
7
8
9
10
11
extends layout

block content
  h1 search login
  .lead please login to search
  br
  a(href='/auth/google') Login with Google

script(src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js")   
script(src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.min.js")
script(src="/javascripts/main.js")

Before we add Mongo, fire up the server and test everything. If you run into an error, be sure to double check your code with my code from this blog post or the repository (link below).

MongoDB

Add/update the following code in “app.js”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// connect to the database
mongoose.connect('mongodb://localhost/craigslist');

// create a user model
var User = mongoose.model('User', {
  oauthID: Number
});

// serialize and deserialize
passport.serializeUser(function(user, done) {
  console.log('serializeUser: ' + user._id)
  done(null, user._id);
});
passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user){
    console.log(user)
    if(!err) done(null, user);
    else done(err, null)
  })
});

// config
passport.use(new GoogleStrategy({
  returnURL: 'http://127.0.0.1:3000/auth/google/callback',
  realm: 'http://127.0.0.1:3000'
},
function(accessToken, refreshToken, profile, done) {
User.findOne({ oauthID: profile.id }, function(err, user) {
 if(err) { console.log(err); }
 if (!err && user != null) {
   done(null, user);
 } else {
   var user = new User({
     oauthID: profile.id,
     created: Date.now()
   });
   user.save(function(err) {
     if(err) {
       console.log(err);
     } else {
       console.log("saving user ...");
       done(null, user);
     };
   });
 };
});
}
));

Test again

Fire up the server, then login.

Next, open a new terminal window and type the following commands:

1
2
3
4
5
6
7
8
9
10
11
$ mongo
MongoDB shell version: 2.4.6
connecting to: test
> use craigslist;
switched to db craigslist
> show collections;
system.indexes
users
> db.users.find({})
{ "_id" : ObjectId("52b5f9ad3aaf9ef010000001"), "__v" : 0 }
>

Here we connected to the Mongo database, craigslist, then searched the collection and found the created user. You should see the same thing if all went well.

Conclusion

Grab the final code from the repo found here. Ask questions. <3 Next time we’ll be taking a step back to create user stories and reorganize our codebase. Perhaps we’ll even get to some testing!

Comments