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
Express.js
By David Fekke
Express.js
These are the slides for the JaxNode Express.js presentation for October 2017
- 1,479