Building A Bakery Web App with Isomorphic JavaScript

JavaScript is awesome, just let me show you

A little bit about me.

Jean-Marcel Belmont

Software Engineer at Interactive Intelligence

JavaScript Ecosystem Appears like a Dumpster Fire

FrontEnd Architecture:

React.js

React Router

Redux (Dispatch actions to Redux Store)

Sass

BackEnd Architecture:

Node.js

Express.js

RethinkDB

This guy is breathing out insane chicken wing sauce

Server starts at bin/www by setting port and grabbing self signed certificate before launching https server.

	
/**
 * Get port from environment and store in Express.
 */
const port = normalizePort(process.env.PORT);
const IP_ADDRESS = process.env.IP_ADDRESS;

const options = {
  key: fs.readFileSync(path.join(__dirname, '../ca/key.pem')),
  cert: fs.readFileSync(path.join(__dirname, '../ca/cert.pem'))
};

app.set('port', port);
const server = https.createServer(options, app);
					

app.js is where logic begins for the Server.

Setting view rendering engine

Set middleware layer to parse json

Set routes that connect with express.js


// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');

app.use(favicon(path.join(__dirname, 'static/images', 'favicon.ico')));
app.use(logger('dev'));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

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

app.use('/', routes);
// Load payments api and admin interface api.
app.use('/api/v1/payments', payments);
app.use('/api/v1/admin', admin);
					

Express.js serves as middleware for node.js wrapping request and response with special properties and methods.

Easily handle CRUD (Create, Read, Update, Delete) operations with express.js

						
router.post('/receivePayment', (req, res, next) => {
    const {stripeToken} = req.body;
    return stripe.charges.create({
        amount: 1000, // Amount in cents
        currency: "usd",
        source: stripeToken,
        description: "Example charge"
    })
    .then(charge => res.send(charge))
    .catch(err => winston.log('error', 'Error Creating Charge for stripe', {err}));
});

module.exports = router;
						
					

Include your own modules by using Node.js commonjs pattern

						
"use strict";
const rethinkdb = require('rethinkdb');
const winston = require('winston');
const recipes = require('./recipes')["recipes"];
const DB = { DATABASE_NAME: process.env.DATABASE_NAME || 'paradiso',
...
};
function dbActions() {
  return connectToRethinkDBServer()
    .then((connection) => {
      DB.connection = connection;
      return doesParadisoExist()
        .then(exists => exists);
    })
	...
}
exports.dbActions = dbActions;
						
					

Here is a simple module that loads environment variables held in a hidden file.

						
module.exports = {
    loadEnvironmentVars: require('dotenv').config()
}
						
					

Webpack is a module bundler that is configurable and has a large plugin ecosystem.

Babel is a JavaScript Transpiler that has many plugins for newer javascript features.

						
const webpack = require('webpack');
module.exports = {
  entry: {
    App: './static/js/components/App.jsx'
  },
  output: {
    path: __dirname + '/static/build',
    filename: '[name].js'
  },
  devtool: 'source-map',
	...
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('development')
      }
    }),
    ...
						
					

Use gulp tasks and intergrate them with npm scripts.

						
// gulpfile here							
gulp.task('start', () => {
  nodemon({
    script: './bin/www',
    ignore: ['static/*'],
    env: { 'PORT': '3000' }
  });
});

gulp.task('build', (cb) => {
  runSequence('copy:react:files', 'uglify:js', 'build:js', 'build:sass', 'build:vendor:sass',  cb);
});
						
					

NPM scripts is where you can handle compilation and other tasks.

							
"scripts": {
		"clean:build": "rimraf ./static/build",
		"lint:watch": "npm run lint -- --watch",
		"lint": "esw static/js",
		"prestart:dev": "npm run clean:build",
		"start:dev": "gulp dev",
		"build": "npm run clean:build && gulp build",
		"test": "jest tests"
  }
							
						

babelrc is where you load up optional presets for babel compilation.

						
// .babelrc 
{ 
    "presets": ["es2015", "react", "stage-0"] 
}
					
					
Now for the FrontEnd Logic:

React.js is a powerful library built for user interfaces

Redux is a predictable state container for JavaScript apps.

React Router is a complete routing library for React.

FrontEnd Architecture for this app begins inside static/js

The folder structure is using a pattern in redux where you store reducers, actions, and the store in their own scope.

There is a some work to figuring how to connect react router, redux and react together.

App Logic begins in App.jsx
						
import React, { Component } from 'react';
...

import Main from './Main.jsx';
import BakeryHome from './BakeryHome.jsx';
import Menu from './Menu.jsx';
...

import store, { history } from '../store/store';
						
					
React Router Snippet
							

    
      
        
        
        
      
      
      
    
  
							
						

In Redux you dispatch actions to the store.

Use reducers to initiate state change.

Component dispatches action to the Redux Store

						
  _addToCart(evt) {
    const item = evt.currentTarget.dataset["recipeName"];
    const hasCartBeenAdded = cart.some(cartItem => {
      return cartItem === item;
    });
    if (!hasCartBeenAdded) {
      cart.push(item);
      cartLength++;
      store.dispatch(addToCart({ 
        item,
        cartLength 
      }));
    }
  }		
						
					

Action is handled here:

						
export function addToCart({item, cartLength}) {
  return {
    type: 'ADD_TO_CART',
    item,
    cartLength
  };
}							
						
					
Mind Blown

Reducer updates state with Redux Store

							
function checkoutCart(state = [], action) {
    const {
        type,
        item,
        cartLength
    } = action;
    
    switch (type) {
        case 'ADD_TO_CART':
            return [
                ...state,
                {
                    cartLength,
                    cartItems: item
                }
            ];
        default:
            return state;
    }
}

export default checkoutCart;	
							
						
Testing in React.js can be done using the Jest Testing Library.

Jest uses a concept called snapshot testing in order to do some integration testing.

							
import renderer from 'react-test-renderer';
import React from 'react';

import MenuSideBar from '../static/js/components/MenuSideBar.jsx';

test('Show Links in Main bakery site', () => {
const menuitems = ['Breakfast', 'Breakfast Ala Carte','Boxed Lunches', 'Entree Salads', 'Gourmet Trays and Appetizers', 'Main Selections', 'Pizzas'];
const tree = renderer.create(

).toJSON();
expect(tree).toMatchSnapshot();
});							
							
						
Unit tests with Jest can be easily done with builtin assert statements.

You can use describe and it blocks to do bdd (behavior-driven development) style assertions.

							
it('should return a token when calling stripe api', (done) => {
stripe.tokens
	.create(card)
	.then(token => {
		const {id} = token;
		stripeCharge["charge"]["source"] = id;
		expect(token["id"]).toBeDefined();
		done();
	})
	.catch(err => {
		if (err) {
			winston.log('error', 'Stripe Token api error', {err})
			done(err);
		}
		done();
	});
});						
							
						
Demo Time!!!