Express.js

JaxNode October 2017

4th Year Anniversary

Topics

  • What is Express?
  • Middleware
  • Routes
  • View Template Engines
  • Testing
  • Future of Express
  • Next.js
  • Cloud hosting

What is Express.js

  • Simple Web Application Framework
  • Provides syntactic sugar on top of Node.js Connect with Routing 
  • Similar to Sinatra or Nancy
  • Many other frameworks built on top of express

History

  • Project started by TJ Holowaychuk 2009
  • Launched on NPM in 2010
  • Doug Williams took over in 2014
  • Briefly run by StrongLoop
  • Now part of the Node.js Foundation
  • Doug Williams runs along with TC

Express.js

  • Very mature framework
  • Many other frameworks based on Express
  • Sails.js, LoopBack, Kraken, Bottr and many more...
  • Supports most JavaScript view frameworks through consolidate.js

Quick Demo

Can the presenter build a express app in 2 lines of code?

// Basic example
const express = require('express');
const app = express();

app.get('/', (req, res) => {
    res.send('Hello World!');
});

app.listen(3000);

Middleware

  • What is middleware?
  • Allows for pre-processing of the request pipeline
  • The less middleware, the quicker the response 
  • Json parsing, Form parsing, Cookie parsing
  • Validation handling
  • Error and missing resource handling
  • Specify a template engine
  • Handle and log errors

Configure middleware

  • app.use((req, res, next) => { doSomething() });
  • Accepts a function that takes request, response and a next handler function 

Handle static assets

const express = require('express');
const app = express();
app.use(express.static(path.join(__dirname, 'public')));

Body Processor

  • Convert request data to:
  • JSON
  • Form
  • Text
const express = require('express');
const app = express();
const bodyParser = require('body-parser');

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

Cookie Handling

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

const cookieParser = require('cookie-parser');
app.use(cookieParser());

404

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

app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

Error Handling

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

// error handler
app.use(function(err, req, res, next) {
    // set locals, only providing error in development
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};

    // render the error page
    res.status(err.status || 500);
    res.render('error');
});

Routes

  • Since Express 4, their own Module
  • Easy to create testable routes
  • app.get('/', (req, res) => { ... });
  • Routes should be kept simple
  • Use services for more complex logic
const express = require('express');
const router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {    
    res.render('index', { title: 'Hello World!' });
});

module.exports = router;

Defining Route Paths

  • '/route-str'
  • Define parameters in URL
  • '/route/:myparam'
  • req.params.myparam
  • Can use RegEx in your route
  • '/ab?cd' will match abcd or acd
  • '/ab+cd' can match abcd, abbcd, abbbcd
  • '/ab*cd' can match abcd, abxcd, abRANDOMcd
  • /a/ can match anything with an 'a' in a route

Testable Routes

  • Better to define route closures as separate modules 
  • router.get('/', function (req, res) { ... } );
  • becomes
  • function myHandler(req, res) { ... }
  • router.get('/', myHandler);

Multiple Handlers

var express = require('express');
var router = express.Router();

// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
  console.log('Time: ', Date.now());
  next();
});
// define the home page route
router.get('/', function (req, res) {
  res.send('Birds home page');
});
// define the about route
router.get('/about', function (req, res) {
  res.send('About birds');
});

module.exports = router;

Request Object

  • Contains the request state
  • Access request headers and parameters 
  • req.params
  • req.query
  • req.body (requires parser)

Response Object

  • res.send() // Send text
  • res.end() // end response
  • res.render() //render a view
  • res.json() // return JSON object
  • res.redirect() // redirect (302, 301)
  • res.download() // Download a file
  • res.sendFile() // return a file (myhome.html)

Views Engine

  • pug (formerly Jade)
  • pug allows for JS logic
  • Handlebars
  • SSR with React
  • Vue.js
  • many others

Pug

extends layout

block content
  mixin link(href, name)
    a(href=href)= name
  
  script(async='async', src='//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js')
  ins.adsbygoogle(style='display: inline-block; width: 728px; height: 90px;', data-ad-client='ca-pub-3395496145928180', data-ad-slot='5814993232')
  script.
    (adsbygoogle = window.adsbygoogle || []).push({});
  h1 Jax Upcoming Tech Events
  p Here are upcoming events for people in the Jacksonville, Florida area interested in learning more about computer programming
    | and computer languages and frameworks. Find your favorite developer and programming user groups on this site.
    | Use this page to find programming and technology user group meetings.
  div.row
    div.col-md-3
      a(href="https://itunes.apple.com/us/app/jax-tech-meetups/id919592266?mt=8", target="_blank") 
        img(src="images/appstore.png", alt="App Store")
    div.col-md-9
      p 
        em Please download our iPhone app. Android and Windows Phone coming soon.

Handlebars

{{!< LAYOUT}}
  <h1>{{title}}</h1>
  <p>
      If you are interested in coming to our meetings or contacting our organizers, 
      please go to our <a href='http://meetup.com/Jax-Node-js-UG/'>Meetup page.</a>
  </p> 
  <p>
      You can also follow us on Twitter and GitHub!
  </p>
  <a href="https://twitter.com/jaxnode" class="twitter-follow-button" data-show-count="false" data-size="large">Follow @jaxnode</a>
  
  <a class="github-button" href="https://github.com/jaxnode" data-style="mega" data-count-href="/jaxnode/followers" data-count-api="/users/jaxnode#followers" data-count-aria-label="# followers on GitHub" aria-label="Follow @jaxnode on GitHub">Follow @jaxnode</a>
  

Rendering Data to a View

// app.js

// Setup view engine
app.set('view engine', 'pug');
// setup route
app.get('/', function (req, res) {
  res.render('index', { title: 'Hey', message: 'Hello there!' })
});

// index.pug
html
  head
    title= title
  body
    h1= message

Service Injection

  • Routes can accept more than one handler
  • app.get('/', service, handler);
  • Will need for testing
const service = require('./services/mainService');
const routes = require('./routes/index');

const exposeService = function (req, resp, next) {
    req.service = service;
    next();
};

app.use('/', exposeService, routes);

Testing

  • Many popular testing Frameworks
  • Mocha, Tape
  • Instanbul code coverage
  • Jest from Facebook
  • Make sure Routes are testable
  • Supertest for http mocking

Test

const request = require('supertest');
const express = require('express');
const routesForApis = require('../routes/apiroutes');
const mockService = require('../services/mockService');
const app = express();

describe('API Routes', function () {

    before(function () {
        var exposeService = function (req, resp, next) {
            req.service = mockService;
            next();
        };

        app.use('/v1/api', exposeService, routesForApis);
    });

    describe('GET Meeting', function () {
        it('responds to /v1/api/meeting', function testApi(done) {
            request(app)
                .get('/v1/api/meeting')
                .expect('Content-Type', /application\/json/)
                .expect(200, done);
        });
    });

});

Future of Express

  • Express 5 in alpha
  • Some response members removed
  • app.router is back
  • Can override query string parsing
  • res.render now enforces async behavior

Express CLI

  • Scaffold an express app from scratch
  • install 'sudo npm i -g express-generator'
  • use 'express' command
  • $ express <name of your app>
  • Can specify view and/or stylesheet engine
  • $ express --hbs --git myapp

Cloud hosting

  • Most Cloud hosting providers support Node
  • Azure, AWS, Google, BlueMix and GoDaddy
  • AWS Lamba has version of express
  • Zeit.co provides very simple site and service hosting through 'now' cli

Demo

Next.js

  • Started by Guillermo Rauch
  • Isomorphic application framework
  • Isomorphic sometimes called Universal
  • Combine Express and React
  • Allows server-side rendering 
  • Ajax calls for subsequent requests
  • Automatic code-splitting

Why Next.js

  • Do you need a SPA?
  • Next.js is based on React.js
  • Next implements separate endpoints for each page 
  • Handles code splitting so you only use the code you need per request

Next.js features

  • Place endpoints into /pages directory
  • Default page will be /pages/index.js
  • Routing module <Link />
  • Added getInitialProps method for first time request
  • History handled autmagically
const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare()
.then(() => {
  const server = express()

  server.get('/p/:id', (req, res) => {
    const actualPage = '/post'
    const queryParams = { id: req.params.id } 
    app.render(req, res, actualPage, queryParams)
  })

  server.get('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})
.catch((ex) => {
  console.error(ex.stack)
  process.exit(1)
})

Next 4

  • React 16
  • styled jsx 2
export default ({ color }) => (
    <div>
      Hello there <span>my friend</span>
      <style jsx>{`
        /* this style only applies to the span within lexical scope */
        span { color: ${color}; }
      `}</style>
    </div>
  )

Demo

Resources

  • expressjs.com
  • slides.com/davidfekke/expressjs
  • github.com/jaxnode-UG/jaxnode
  • zeit.co/blog/next4

Contact Me

  • David Fekke
  • @davidfekke @jaxnode on Twitter
  • Skype davidfekke
  • email davidfekke at gmail dot com
Made with Slides.com