Web Development with Node.js
LV 02
Daniel Khan / daniel@khan.io / University of Applied Sciences Hagenberg / KWM
The Node Package Manager npm
Important Commands
# Important commands
# Update npm itself
$ npm install -g npm@latest
# Initialize a project by creating a package.json file
$ npm init
## Try: npm init
# Install a package. Use -g to install globally
# --save / --save-dev adds the package to package.json, -g installes globally
$ npm install [-g] [--save | --save-dev] <package name>
## Try: npm install --save express
## Try: npm install --save-dev mocha
# Update a package
$ npm update <package name>
# Uninstall a package
# --save / --save-dev also removes it from package.json
$ npm uninstall [--save | --save-dev] <package name>
## Try: npm uninstall --save-dev mocha
# List all dependencies / installed packages
$ npm [-g] ls
## Try: npm ls and npm ls -g
# Scan for security problems
$ npm audit
## Try: npm audit
package.json
{
"name": "myaweseomepackage",
"version": "1.0.0",
"description": "This is a package that is awesome.",
"main": "index.js",
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1",
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.3",
},
"devDependencies": {
"mocha": "^5.1.1",
}
}
git: Push to version control!
Semantic versioning
// package.json
// Matches 4.16.2, 4.16.3, 4.16.4, ...
"dependencies": {
"express": "~4.16.2",
}
// Matches 4.16.x, 4.17.x, 4.18.x, ...
"dependencies": {
"express": "^4.16.2",
}
// Matches 4.x.x, 5.x.x, 6.x.x, ...
"dependencies": {
"express": "*",
}
package-lock.json
{
"name": "myaweseomepackage",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
// ...
}
git: Push to version control!
Express.js
Vanilla JSON Server
const http = require('http');
const url = require('url');
const portNumber = process.argv[2];
http.createServer(function (req, res) {
var urlObject = url.parse(req.url, true),
pathname = urlObject.pathname,
startTime = urlObject.query.iso, result;
if (pathname === '/api/unixtime') {
result = getUnixTimeStamp(startTime);
} else if (pathname === '/api/parsetime') {
result = getTimeObj(startTime);
}
if (result) {
res.writeHead(200, { 'Content-type': 'application/json' });
res.end(JSON.stringify(result));
} else {
res.writeHead(404);
res.end();
}
}).listen(portNumber)
function getUnixTimeStamp(startTime) {
return {
unixtime: getTimeStamp(startTime)
};
}
function getTimeStamp(startTime) {
return Date.parse(startTime);
}
function getTimeObj(startTime) {
var date = new Date(getTimeStamp(startTime));
return {
hour: date.getHours(),
minute: date.getMinutes(),
second: date.getSeconds()
};
}
const express = require('express');
const app = express();
const portNumber = process.argv[2];
function getUnixTimeStamp(startTime) {
return {
unixtime: getTimeStamp(startTime)
};
}
function getTimeStamp(startTime) {
return Date.parse(startTime);
}
function getTimeObj(startTime) {
var date = new Date(getTimeStamp(startTime));
return {
hour: date.getHours(),
minute: date.getMinutes(),
second: date.getSeconds()
};
}
app.get('/api/unixtime', (req, res) => {
const startTime = req.query.iso;
const result = getUnixTimeStamp(startTime);
return res.json(result);
});
app.get('/api/parsetime', (req, res) => {
const startTime = req.query.iso;
const result = getTimeObj(startTime);
return res.json(result);
});
app.listen(portNumber, () => console.log(`Example app listening on port ${portNumber}!`));
Express Version
Express Basics
Routing
// respond with "hello world" when a GET request is made to the homepage
app.get('/', function (req, res) {
res.send('hello world')
});
// POST method route
app.post('/', function (req, res) {
res.send('POST request to the homepage')
});
// Called for every route
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...')
next(); // pass control to the next handler
});
// Will match abcd
app.get('/abcd', function (req, res) {
res.send('abcd');
})
// This route path will match /abe and /abcde.
app.get('/ab(cd)?e', function (req, res) {
res.send('abcd');
})
Route Handlers
Parameters
const express = require('express');
const app = express();
// Path that takes an user id
app.get('/user/:userId', function (req, res) {
res.send(`You requested user ${req.params.userId}`)
});
app.get('/user/:userId/posts/:postId?', function (req, res) {
if (req.params.postId) {
res.send(`You requested post ${req.params.postId} for user ${req.params.userId}`)
} else {
res.send(`You requested all posts for user ${req.params.userId}`)
}
});
Middleware
const myLogger = function (req, res, next) {
console.log('LOGGED')
next();
}
app.use(myLogger);
Express Router
const routes = require('./routes');
app.use('/', routes));
In app.js
const express = require('express');
const router = express.Router();
router.use('/frontend', require('./frontend'));
router.use('/admin', require('./admin'));
router.use('/user', require('./user'));
router.use('/protected', require('./protected'));
module.exports = router;
In ./routes.index.js
const express = require('express');
const router = express.Router();
// Matches for GET /frontend
router.get('/', (req, res) => {
res.send('Hello from frontend!');
});
module.exports = router;
In ./routes/frontend/index.js
Application & Request Lifecycle
const express = require('express');
const app = express();
const portNumber = process.argv[2];
app.use((req, res, next) => {
if (!app.locals.count) {
app.locals.count = 1;
} else {
app.locals.count++;
}
return next();
});
app.use((req, res, next) => {
req.requestTime = Date.now();
return next(); // Important!
});
app.use((req, res, next) => {
console.log(`Request #${app.locals.count} for ${req.url} at ${req.requestTime}`);
return next(); // Important!
});
// Stop
app.get('/',
(req, res, next) => {
app.locals.count % 2 == 0 ?
next() :
res.status(400).json({ 'result': 'You shall not pass' });
},
(req, res) => {
res.json({ 'result': 'Welcome friend!' });
}
);
// Stop
app.get('/podbay', (req, res, next) => {
return next('Sorry Dave, I can\'t not do that');
});
// Stop
app.get('/podbay', (req, res, next) => {
return next('Sorry Dave, I can\'t not do that');
// return next(new Error('Sorry Dave, I can\'t not do that'));
});
// Error handler
app.use( (err, req, res, next) => {
console.error(err);
res.status(500).send('Something broke!')
})
app.listen(portNumber, () =>
console.log(`Example app listening on port ${portNumber}!`));
Ending a Request
Let's create a first sample application
// Install express generator globally
$ npm install express-generator -g
// Create the express application in a folder 'express-sample'
$ express --view=ejs express-sample
// Install all dependencies
$ npm install
$ npm install --save-dev eslint
$ ./node_modules/eslint/bin/eslint --init
# airbnb
# no React
# JavaScript
# Install Visual Studio Code eslint module
# Fix all errors
Enable Linting
$ npm install --save-dev mocha chai chai-http
# Create directory 'tests'
Set-Up Tests
"scripts": {
"start": "node ./bin/www",
"test": "eslint . --ext .js && mocha --recursive tests/"
},
package.json
const chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
chai.should(); // Function!!!
const { expect } = chai; // Reference!!
const server = require('../../app');
describe('GET /', () => {
it('should GET all the books', (done) => {
chai.request(server)
.get('/')
.end((err, res) => {
res.should.have.status(200);
expect(res.text).to.be.a('string');
expect(res.text).to.contain('Express');
done();
});
});
});
tests/routes/index.js
Set-Up MongoDb
# Install Docker
$ docker run --name mongo-testserver -d -p 28018:27017 mongo
# MongoDb is now available on localhost port 28018
# Install Mongo 3T
$ npm install --save mongoose
//Import the mongoose module
var mongoose = require('mongoose');
//Set up default mongoose connection
var mongoDB = 'mongodb://127.0.0.1/my_database';
mongoose.connect(mongoDB);
// Get Mongoose to use the global promise library
mongoose.Promise = global.Promise;
//Get the default connection
var db = mongoose.connection;
//Bind connection to error event (to get notification of connection errors)
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
KWM18 LV02
By Daniel Khan
KWM18 LV02
- 839