Express Routers, Middleware, Databases, Credit Cards




CS174

Chris Pollett

May 12, 2021

Outline

Patterns and variables in Express Routes

var express = require('express');
var app = express();
app.get('/', function (req, res) {
    res.send('The Love Web App');
});
// routes can match a string expressions involving *, ? (, )
app.get('/*love*', function (req, res) {
    res.send('There are many routes to love.<br />' +
        'Yours was ' + req.path);
});
// You can also use regular expressions
app.get(/l(u|a)+v(e)?$/, function (req, res) {
    res.send('Your route ends with ' + req.path + "<br />"+
        'Your regex capture groups were [0]=>' +   req.params[0] +
        " [1] =>"+ req.params[1]);
    console.log(req);
});
// this example shows getting req parameter from a pattern
app.get('/luv/:from-:to', function (req, res) {
    var from =  (typeof req.params.from !== 'undefined')
        ? req.params.from : "Everyone";
    var to =  (typeof req.params.to !== 'undefined') ?
        req.params.to : "someone";
    res.send(from + ' sends their love to ' + to);
});
// You can specify a sequence of handlers each as separate arguments
app.get('/lost', function (req, res, next) {
        console.log(req.ip + " seems lost in love!");
        next()
    }, function (req, res) {
       console.log("Redirecting love to a more fruitful place...");
       res.redirect(301,'/love'); 
    }
);
// Or you can specify an array of functions
var lust1 = function (req, res, next) {
    console.log(req.ip + " has confused lust for /love");
    next(); // call the next handler
}
var lust2 = function (req, res) {
       console.log("Redirecting lust to love");
       res.redirect(301,'/love'); 
    }
app.get('/lust', [lust1, lust2]);
//You can combine requests to the same url path but different methods as
app.route('/form').get(function(req,res) {
    res.send('<form method="post"><button>Submit</button></form>');
}).post(function(req, res) {
    res.send('You submitted the love form');
});
//start app listening
app.listen(8888, function () {
    console.log('Server up!')
});

More on Router Modules

More on Modules

Express Middleware

In-Class Exercise

Node Database Integration

Node Database Integration - continued

Remarks on Database Code

Prepared Statements

var mysql = require('mysql')
// set-up and connect
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : 'root',
  database : 'FOO'
});
if (typeof process.argv[2] === 'undefined') {
    console.log('This command should be run with a line like:\n' +
        'node prepare_db.js some_number');
    process.exit();
}

connection.connect();
/* For strings we can use ?? if we want the formatter to escape them
   If we have a variable and we want it interpolated with the same type,
   without escaping, we use ?.
 */
var first_out = parseInt(process.argv[2])
var sql = mysql.format('SELECT ?? FROM TEST LIMIT ?',
    ["ID", first_out]);
connection.query(sql,
    function (error, results, fields) {
        if (error) throw error;
        console.log(results); // the complete result set
    }
);
/*
  Like many node object, a query also generates events when
  things happen, such as a single row in the results has been received.
  We can handle these using the "on" method.
  Notice below we also skip the mysql.format step
 */
var query = connection.query('SELECT ID FROM TEST LIMIT ?', [first_out]);
query.on('result', function(row) {
    // Pause so can do I/O without more events
    connection.pause();
    for (var elt in row) {
        console.log( elt + ":" + row[elt]);
    }
    connection.resume();
}).on('end', function() {
    connection.end();
});

setTimeout, setInterval, setImmediate

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

app.get('/', function (req, res) {
  res.send('Default landing page')
})
// set up rest of app routes

app.listen(8888, function () {
  console.log('Server up!')
});

// background task that gets 
setInterval(function() {
    console.log("Hi there, I'm the cool background task!\n");
}, 5000);
  • There is also a setImmediate function in Node, which queues the callback for immediate execution (i.e., like setTimeout with 0 delay).
  • Sending Email

    Credit Card Transactions

    Stripe

    Stripe Example - Project Structure

    app.js
    config.js
    node_modules 
        | stuff installed via npm
    package.json
    views
        | index.ejs
        | message.ejs
    

    Stripe Example -- Intuition

    Stripe Example - index.ejs

    <!DOCTYPE html>
    <html>
    <head><title>Credit Card Test</title></head>
    <body>
    <form id="purchase-stuff-form" method="post" action="/charge">
    <input type="hidden" id="credit-token"  name="credit_token" value="" />
    <p><label for="amount">Amount:</label><input type="text" id="amount"
        size="2" name="amount" /></p>
    <p><label for="card-number">Card Number:</label><input type="text"
        id="card-number" size="20" data-stripe='number'
        name="card-number" /></p>
    <p><label for="cvc">CVC:</label><input type="text" id="cvc" size="4"
        data-stripe='cvc' name="cvc" /></p>
    <p><label for="exp-month">Expiration Month:</label><input type="text"
        id="exp-month" size="2" data-stripe='exp-month' name="exp-month" /></p>
    <p><label for="exp-year">Expiration Year:</label><input type="text"
        id="exp-year" size="2" data-stripe='exp-year' name="exp-year" /></p>
    <p><input type="submit" id="purchase" name="Purchase" value="Purchase"></p>
    </form>
    <script>
    function elt(id)
    {
        return document.getElementById(id);
    }
    elt('purchase').onclick = function(event) {
        var purchase_form = elt('purchase-stuff-form');
        elt('purchase').disabled = true; // prevent additional clicks
        Stripe.card.createToken(purchase_form, tokenResponseHandler);
        event.preventDefault(); //prevent form submitting till get all clear
    }
    function tokenResponseHandler(status, response) 
    {
        var purchase_form = elt('purchase-stuff-form');
        if (response.error) {
            alert(response.error.message);
            elt('purchase').disabled = false;
        } else {
            elt('credit-token').value = response.id;
            purchase_form.submit();
        }
    }
    </script>
    <script src="https://js.stripe.com/v2/"  ></script>
    <script>
    Stripe.setPublishableKey('<%=PUBLISHABLE_KEY %>');
    </script>
    </body>
    </html>
    

    Stripe Example - message.ejs

    <!DOCTYPE html>
    <html>
    <head><title>Credit Card Test - <%=message %></title></head>
    <body>
    <h1><%=message %></h1>
    </body>
    </html>
    

    Stripe Example - config.js

    var config = {
        "SECRET_KEY": "sk_test_key",
        "PUBLISHABLE_KEY": "pk_test_key",
        "CHARGE_URL": "https://api.stripe.com/v1/charges",
        "CHARGE_CURRENCY": "usd",
        "CHARGE_DESCRIPTION": "Buyer sees this on their statement",
        "CHARGE_USERAGENT": "CreditCardTester",
        "TIMEOUT": 20
    }
    module.exports = config;
    

    Stripe Example - app.js

    var express = require('express');
    
    var body_parser = require('body-parser'); //to handle posted data
    var path = require('path'); // for directory paths
    var config = require(path.join(__dirname, 'config')); // has our keys
    var request = require('request'); // to make backend requests to stripe
    
    var app = express();
    app.use(body_parser.urlencoded({extended: true}));
    
    // view engine setup
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'ejs');
    
    app.get('/', function(req, res) {
      res.render('index', { 'PUBLISHABLE_KEY': config.PUBLISHABLE_KEY });
    });
    app.post('/charge', function(req, res) {
        /*here we use the request module to make a stripe request using
          the token we received from our form*/
        request.post({
            url:config.CHARGE_URL, 
            form: {
                //swipe charges in cents * 100 to convert to dollars
                "amount": req.body.amount * 100,
                "currency": config.CHARGE_CURRENCY,
                "source": req.body.credit_token,
                "description": config.CHARGE_DESCRIPTION
                },
            auth: {
                'user': config.SECRET_KEY,
                'pass': ''
                }
            },
            function(err, http_response, body) {
                stripe_result = JSON.parse(body);
                if (typeof stripe_result.status === 'undefined') {
                    if (typeof stripe_result.message === 'undefined') {
                        res.render('message', { 'message': req.body.amount +
                            "charge did not do through!<br />" +
                            stripe_result.credit_message});
                    }
                } else if (stripe_result.status == 'succeeded') {
                    res.render('message', { 'message': req.body.amount +
                        "charged" });
                }
            }
        );
    });
    app.listen(8888, function () {
        console.log('Credit Server up!')
    })