Blog: May 30 2018

Using Fetch in a React/ Node app

Twitter Sentiment Is a personal project built with React and Node. The app uses natural language processing sentiment analysis APIs to determine if a tweet is negative, positive or neutral.

How the app works:

1. A query is submitted from the user (Either a hashtag, or a Twitter Username)

2. The query is passed to the Node server using the Fetch API

3. The server calls the Twitter api with the query

4. Twitter returns the tweets from the username or hashtag to the server

5. The server returns the tweets to the browser

6. While the tweets are rendering, the text content from each tweet is passed from the Node server to two other APIs (Google NLP and Datumbox) to get the sentiment analysis.

7. The tweet sentiments are returned from the APIs, passed to the front-end and rendered in the browser

Passing the query from the user (client side) to the Node backend

                 if (fieldType === 'screename') {
                   const screename = e.target.elements.screename.value;
                   // fetch my api end point (which calls the Twitter api)
                   fetch('/getTweets', {
                     method: 'POST',
                     body: JSON.stringify({
                       screename
                     }),
                     headers: {
                       'Content-Type': 'application/json',
                       'Accept': 'application/json'
                      }
                   })
                   .then(res => res.json())
                   .catch(err => {
                      console.error(err);
                   })
                   .then(tweets => {
                     // pass tweets to my tweet components for rendering
                     if (tweets) passTweets(tweets);
                   });
                 } 
                 ...

                 // After rendering the tweets, get the tweet sentiments
                 getSentiment(tweets) {
                   tweets.forEach((tweet) => {
                     fetch('/googleSentiment', {
                       method: 'POST',
                       body: JSON.stringify({
                         tweetText: tweet
                       }),
                       headers: {
                         'Content-Type': 'application/json',
                         'Accept': 'application/json'
                       }
                     })
                     .then(res => res.json())
                     .catch(err => {
                       console.error(err);
                     })
                     .then(sentiment =>
                       this.setState(prevState => ({
                         googleSentiments: [...prevState.googleSentiments, sentiment]
                       }))
                     );
               

Calling the Twitter and Sentiment Apis

                app.post('/getTweets', (req, res) => {
                  const Twitter = new Twit({
                    consumer_key: config.twitter.consumer_key,
                    consumer_secret: config.twitter.consumer_secret,
                    access_token: config.twitter.access_token,
                    access_token_secret: config.twitter.access_token_secret
                  });
                  // the query passed from the client
                  const screename = req.body.screename;
                  Twitter.get('statuses/user_timeline', {
                    screen_name: screename, count: 10, method: 'GET'
                  }).catch((err) => {
                      console.log(err);
                    })
                    .then((result) => {
                      const tweets = result.data;
                      res.json(tweets)
                    })
                });

                app.post('/getHashtags', (req,res) => {
                  const Twitter = new Twit({
                    consumer_key: config.twitter.consumer_key,
                    consumer_secret: config.twitter.consumer_secret,
                    access_token: config.twitter.access_token,
                    access_token_secret: config.twitter.access_token_secret
                  });
                  const hashtag = req.body.hashtag;
                  Twitter.get( 'https://api.twitter.com/1.1/search/tweets.json', {
                    q: `%23${hashtag}`, count: 10 })
                    .catch((err) => {
                      console.log(err);
                    })
                    .then((result) => {
                      const tweets = result.data.statuses;
                      res.json(tweets)
                    })
                });

                app.post('/googleSentiment', (req, res) => {
                  const tweetText = req.body.tweetText;
                  const API_KEY = config.google.apiKey;
                  const nlp = new NLP(key=API_KEY);
                  nlp.analyzeSentiment(tweetText)
                    .then((result) => {
                      const sentiment = result.documentSentiment;
                      res.json(sentiment);
                    }).catch((err) => {
                      return err;
                    });
                });

                app.post('/datumBoxSentiment', (req,res) => {
                  const tweetText = req.body.tweetText;
                  datum.twitterSentimentAnalysis(tweetText, (err, sentiment) => {
                  …
               

Deploying the app

From the create-react-app docs:

“You don’t necessarily need a static server in order to run a Create React App project in production. It works just as fine integrated into an existing dynamic one.”

“However this is not quite enough if you use client-side routing.”

This seemed like the best solution for a single page app, so I'm using the express to run the react build, as opposed to running it on a separate static server/ port


app.use(express.static(path.join(__dirname, 'build')));

I’m using pm2 to run the app ‘forever’, so the command I use is ‘pm2 start node server.js’

Blog: Feb 24 2016

Node with a persistent connection to mySql, passport.js and bcrypt for User Authentication

The following code demonstartes using Sql, Node.js and Passport.JS for user authentication. In addition to using Passport with SQL, I will also show how to pool SQL for multiple connections, and how to pass this connection to multiple routes.

The site that I'm using in this tutorial is NodeHire, a place for both Developers and Business owners to post and apply for Node.js Development jobs. NodeHire is 100% free and eliminates the hassle of finding Node.js work for both parties.

First I will show how to use the local strategy with Passport.js using MySql. To verify the username and password, they need to be compared against the username and password stored in your MySQL table. The first step is to pass the username from the login form into a SQL query to your database. This query then returns the users previously hashed and saved password. Once the hashed password is retrieved, it can then be passed to the bcrypt password compare function to compare versus the unhashed input from the user. If there is a match, the user is logged into the application and able to access their data.

The code below shows how I connect with MySql, set up my passport local strategy, query the database, and validate the user.

                passport.serializeUser(function(user, done) {
                  done(null, user);
                });

                passport.deserializeUser(function(user, done) {
                  done(null, user);
                });

                router.post('/login', passport.authenticate('local', {
                  failureRedirect: '/',
                  failureFlash: 'Wrong Username or Password'
                }), function(req, res) {
                  req.flash('success', 'You are now logged in');
                  res.redirect('/' /*, { "results": user } */ );
                });

                passport.use(new LocalStrategy(
                  function(username, password, done, req, flash) {
                    console.log('in users the username is ' + username)
                    main.getConnection(function(err, connection) {
                      if (err) {
                        console.log(err);
                      }
                      connection.query('SELECT password FROM nodeusers WHERE username = ?', username, function(err, user) {
                        console.log(user + 'got user')
                        parseResults(user, done);
                      });
                    });

                    function parseResults(user, done) {
                      Object.keys(user)[0];
                      var key = Object.keys(user)[0];
                      user[key];
                      var storedPw = user[key];
                      for (var i in storedPw) {
                        returnedPw = storedPw[i];
                      }
                      console.log('returnedPw is defined here ' + returnedPw);
                      if (returnedPw === undefined) {
                        return done(null, false, {
                          message: 'Invalid Password'
                        })
                      }
                      movePw(returnedPw, done);
                    }

                    function movePw(returnedPw, done) {
                      User.comparePassword(password, returnedPw, function(err, isMatch) {
                        if (isMatch) {
                          user = username;
                          return done(null, user);

                        } else {
                          console.log('Invalid Password');
                          req.flash('failureFlash', 'User Access Denied. False Password');
                          return done(null, false, {
                            message: 'Invalid password'
                          });
                        }
                      });
                    }
                  }
                ));


                router.get('/logout', function(req, res) {
                  req.logout();
                  // Success Message
                  req.flash('success', "You have logged out");
                  res.redirect('/');
                });

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


In the code above main.getConnection() is the variable which contains the MySql connection stored in an external file, main = require('..main.js'). This variable allows us to connect to SQL and perform querys in an efficient way.

In Main.Js (below) there is a Pooled connection to SQL, and an exported function that will make this connection accessible to all of our routes. Pooling the connections and making the connection in one file, and then passing this connection to all of the applications routes is more effective than creating a new sql connection and connecting independently from each route. The pooled connection is made available to other routes through an exported function. Once a route sends a query to the connection and it is completed, the connection is no longer needed and is released back into the pool.

                const mysql = require('mysql');
                let main;

                function connectDatabase() {
                  // if the pool doesn't exist, create it
                    if (!main) {
                      // enter your db config here
                      var main = mysql.createPool({
                      host : '',
                      user : '',
                      password : '',
                      database : ''
                    });
                  
                    main.getConnection(function(err){
                      if(!err) {
                        console.log('Database is connected!');
                      } else {
                        console.log('Error connecting database!');
                      }
                    });
                    return main;
                }

                //export the function above so that all of our routes can include this script, and call main.GetConnection whenever they need a connection.
                module.exports = connectDatabase();
                


Below is another example of how this connection is accessed in a route. The jobs route is what allows users to submit Jobs, each job is tied to the user by their username. Jobs can then be accepted by other users. This code demonstrates how the job is accepted or registered to another user. The job object has a field for the founder of the job (the business owner or job poster) and another field for the person who registers or accepts the job. To avoid SQL injection, we are passing the values of the user and the bid id into the SQL query using ?. Since we are using multiple ? we need to pass these variables in as an array. Once again we are accessing our main variable to get the connection.

                router.post('/register', (req, res) => {
                  req.flash('success', 'You are now registered');
                  var BidId = req.body.bidId;
                  main.getConnection(function ( err, connection) {
                    connection.query('UPDATE nodejobs SET users = ? WHERE bid_id = ?', [user, BidId] , (err, results) => {
                      if(err){
                        return err;
                      };
                      res.render('bids/index' , { "results": res });
                    });
                   });
                });

                


I hope this is helpful to anyone using SQL with node and user authentication. Follow me on twitter at @seanquinn781