Express Yourself Deux
Intro to the ExpressJS Web Application Framework
What is ExpressJS?
Express is a minimalist, un-opinionated web application framework written in NodeJS for building web applications.
It gives us the ability to get a web application up and running quickly and supports basic concepts related to the web without enforcing too much structure or adding too many features that may not be used in every application.
It is very good at allowing us to handle the different types of HTTP requests and setting up the routes for our application with the ability to be easily extended via NPM packages for other features like cookies, sessions, form inputs, etc...
What is a Framework?
Per dictionary.com a framework is defined as :
an essential supporting structure of a building, vehicle, or object.
a basic structure underlying a system, concept, or text.
What is a Web Application Framework?
Therefore we can define a web application framework as :
A basic structure (or underlying pre-existing code) that supports the commonalities of the necessary components that make up a web application
This includes things like:
- Http/Https
- Routing
- Sessions
- Cookies
- Templating
- and more...
- Express is a node.js web server project
- A routing plus sugar layer on top of Node.js HTTP server
- Declarative routing
- Basic middleware pattern
Installation
//Create the project directory
$ mkdir myapp
$ cd myapp
//Initialize the application
$ npm init
entry point: (index.js) -> Change to app.js
//Install Express - it is a NPM module like anything else
$ npm install express --save
//Create our app.js entry point as defined in package.json
$ touch app.js
First we create a new NPM project and install the Express NPM module
Create our App
const express = require('express')
const app = express()
app.get('/', function(req, res) {
res.send('Hello World!')
})
const server = app.listen(3000, () => {
const host = server.address().address
const port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
Next we add a few basic constructs to create our web application
And to run your application:
#Using node
$ node app.js
#Using nodemon
$ nodemon app.js
Try it out: http://localhost:3000
Adding more routes
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!'))
app.get('/about', (req, res) => res.send('We are awesome!'))
app.get('/faq', (req, res) => res.send('What you need?'))
const server = app.listen(3000, () => {
const host = server.address().address
const port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
We can easily add more routes by adding more "route handlers" where the method is the HTTP method of the request:
Adding moooore routes
// respond with contact form on the contact page
app.get(
'/contact',
//Example shorthand text for HTML form
(req, res) => res.send('...<form>....')
)
// accept POST request on the contact page
app.post('/contact', (req, res) => res.send('Got a POST request'))
// accept PUT request at /user
app.put('/user', (req, res) => res.send('Got a PUT request at /user'))
// accept DELETE request at /user
app.delete('/user', (req, res) => res.send('Got a DELETE request at /user'))
Like we said the method reflects the HTTP verb of the type of request our application has received so we can easily handle any type of request:
Serving static files
// respond with contact form on the contact page
app.use(express.static('public'))
Our application will also serve images, JavaScript and CSS files. We don't need Node to "process" these requests so we tell Express to "use" a static directory that we define as the location to look for these requests.
Express is smart enough to know what kind of requests are static files and to look here first for those requests.
This saves your server processing cycles for not having to handle these requests by cycling through lines of code or writing a route handler for every file imagineable.
Our basic app
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!'))
app.get('/about', (req, res) => res.send('We are awesome!'))
app.get('/faq', (req, res) => res.send('What you need?'))
// respond with contact form on the contact page
app.get(
'/contact',
//Example shorthand text for HTML form
(req, res) => res.send('...<form>....')
)
// accept POST request on the contact page
app.post('/contact', (req, res) => res.send('Got a POST request'))
// accept PUT request at /user
app.put('/user', (req, res) => res.send('Got a PUT request at /user'))
// accept DELETE request at /user
app.delete('/user', (req, res) => res.send('Got a DELETE request at /user'))
const server = app.listen(3000, () => {
const host = server.address().address
const port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
using chaining...
const express = require('express')
const app = express()
app
.get('/', (req, res) => res.send('Hello World!'))
.get('/about', (req, res) => res.send('We are awesome!'))
.get('/faq', (req, res) => res.send('What you need?'))
// respond with contact form on the contact page
.get(
'/contact',
//Example shorthand text for HTML form
(req, res) => res.send('...<form>....')
)
// accept POST request on the contact page
.post('/contact', (req, res) => res.send('Got a POST request'))
// accept PUT request at /user
.put('/user', (req, res) => res.send('Got a PUT request at /user'))
// accept DELETE request at /user
.delete('/user', (req, res) => res.send('Got a DELETE request at /user'))
const server = app.listen(3000, () => {
const host = server.address().address
const port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
Routing +
Advanced Route Handling
If we have multiple HTTP methods for a single route we can use app.route and chain our methods for handling a single URL path for multiple types of requests
app
.route('/book')
.get((req, res) => res.send('Get a random book'))
.post((req, res) => res.send('Add a book'))
.put((req, res) => res.send('Update the book'))
Routing +
Advanced Route Matching
We can also use RegEx for matching patterns of routes to all be handled by a single route handler
app
// will match acd and abcd
.get('/ab?cd', (req, res) => res.send('ab?cd'))
// will match abcd, abbcd, abbbcd, and so on
.get('/ab+cd', (req, res) => res.send('ab+cd'))
// will match abcd, abxcd, abRABDOMcd, ab123cd, and so on
.get('/ab*cd', (req, res) => res.send('ab*cd'))
// will match /abe and /abcde
.get('/ab(cd)?e', (req, res) => res.send('ab(cd)?e'))
Routing +
We don't really want to pollute our main app.js file with a ton of routes so it's best practice to create modules that export our routes for larger applications
Route File Structure
//File path: {{project_directory}}/routes/users.js
var express = require('express');
var router = express.Router();
//GET http://localhost:3000/users
router.get('/', function(req, res) {
res.send('All users');
});
//GET http://localhost:3000/users/1
router.get('/:id', function(req, res) {
res.send('Some dude');
});
//POST http://localhost:3000/users
router.post('/', function(req, res) {
res.send('Creating a dude');
}
//PUT http://localhost:3000/users/1
router.put('/:id', function(req, res) {
res.send('Updated a dude');
}
module.exports = router;
var users = require('./routes/users');
.
.
.
//Defer all routes matching
//http://localhost:3000/users to module
app.use('/users', users);
.
.
.
Responses
Every HTTP request is designed to return an HTTP response and Express gives us methods to send multiple types of responses:
res.download() | Prompt a file to be downloaded. |
res.end() | End the response process. |
res.json() | Send a JSON response. |
res.jsonp() | Send a JSON response with JSONP support. |
res.redirect() | Redirect a request. |
res.render() | Render a view template. |
res.send() | Send a response of various types. |
res.sendFile | Send a file as an octet stream. |
res.sendStatus() | Set the response status code and send its string representation as the response body. |
Things to know:
app.locals - Used for defining application level variables that are accessible anywhere in your application
Express has gone through major revisions between version 3.x and 4.x. When looking at documentation make sure it is referencing the proper version you are using.
Even though we don't handle HTTP headers manually as we did previously, they are still accessible on the req object. Take a look at the docs for the Request/Response objects to see what properties are available.
Testing
const chai = require('chai')
const expect = require('chai').expect
chai.use(require('chai-http'))
const app = require('../index.js') // Our app
describe('API endpoint /colors', function() {
this.timeout(5000) // How long to wait for a response (ms)
// GET - List all colors
it('should return all colors', function() {
return chai
.request(app)
.get('/colors')
.then(function(res) {
expect(res).to.have.status(200)
expect(res).to.be.json
expect(res.body).to.be.an('object')
expect(res.body.results).to.be.an('array')
})
})
})
Resources
Architecture
Wanna build your own?
Text
Express Yourself Deux
By Jason Sewell
Express Yourself Deux
Intro to the ExpressJS Web Application Frameworks
- 431