Express Routers, Middleware, Databases, Credit Cards




CS174

Chris Pollett

Nov 28, 2022

Outline

Patterns and variables in Express Routes

var express = require('express');
var app = express();
app.get('/', function (req, res) {
    res.setHeader('Server', 'My-Lovely-Server');//send a HTTP sresponse header
    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

Quiz

Which of the following statements is true?

  1. The Secure Socket Layer was developed to prevent target blank attacks.
  2. Varnish is kind of CDN.
  3. Asynchronous I/O can be used to alleviate the C10k Problem.

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!')
    })