HTTP Classes
Making a fetch request
const http = require('http');
const options = {
hostname: 'jsonplaceholder.typicode.com',
port: 80,
path: '/todos/1',
};
http.get(options, (res) => {
res.pipe(process.stdout);
});
const http = require('http');
const url = 'http://jsonplaceholder.typicode.com/todos/2';
http.get(url, {}, (res) => {
res.pipe(process.stdout);
});
Creating a server
const http = require('http');
const server = http.createServer((req, res) => {
console.log(req.url);
res.end('done\n');
});
server.listen(8080);
Fast, unopinionated, minimalist web framework for Node.js
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(3000,
() => console.log('Example app listening on port 3000!'));
The view defines how the app's data should be displayed.
The controller contains logic that updates the model and/or view in response to input from the users of the app.
Express' application generator
quickly create an application skeleton
$ npm install -g express-generator
$ express --view=ejs /tmp/foo
$ cd /tmp/foo
$ npm install
$ npm start
.
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.pug
├── index.pug
└── layout.pug
app.METHOD(PATH, HANDLER);
Where:
app.get('/', function (req, res) {
res.send('Hello World!')
});
app.post('/', function (req, res) {
res.send('Got a POST request')
});
app.put('/user', function (req, res) {
res.send('PUT request at /user')
});
app.delete('/user', function (req, res) {
res.send('DELETE request at /user')
});
Node.js body parsing middleware.
Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
app.get('/ab?cd', function (req, res) {
res.send('ab?cd')
})
app.get('/ab+cd', function (req, res) {
res.send('ab+cd')
})
app.get(/.*fly$/, function (req, res) {
res.send('/.*fly$/')
})
matches acd and abcd
matches abcd, abbcd, abbbcd, and so on.
match butterfly and dragonfly, but not butterflyman, dragonflyman, and so on.
app.get('/users/:userId/books/:bookId', function (req, res) {
res.send(req.params)
})
Route path: /users/:userId/books/:bookId
Request URL: http://localhost:3000/users/34/books/8989
req.params: { "userId": "34", "bookId": "8989" }
Route path: /user/:userId(\d+)
Request URL: http://localhost:3000/user/42
req.params: {"userId": "42"}
To have more control over the exact string that can be matched by a route parameter, you can append a regular expression in parentheses (()):
express.Router
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
var birds = require('./birds');
app.use('/birds', birds)
The app will now be able to handle requests to /birds and /birds/about, as well as call the timeLog middleware function that is specific to the route.
An Express application is essentially a series of middleware function calls.
Middleware functions can perform the following tasks:
An Express application can use the following types of middleware:
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
});
app.get('/user/:id', function (req, res, next) {
res.send('USER')
});
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
});
app.use(cookieParser());
Application-level
Application-level
Error-Handling
Third-Party middleware
// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next router
if (req.params.id === '0') next('route')
// otherwise pass control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// render a regular page
res.render('regular')
});
Router-level
Built-in
Express has the following built-in middleware functions:
Express supports many template engines -
A template engine enables you to use static template files in your application.
At runtime, the template engine replaces variables in a template file with actual values, and transforms the template into an HTML file sent to the client.
This approach makes it easier to design an HTML page.
and many more ...
app.set('view engine', 'pug');
app.get('/', function (req, res) {
res.render('index', {
title: 'Hey',
message: 'Hello there!'
});
});
To render template files, set the following application setting properties:
views, the directory where the template files are located. Eg: app.set('views', './views'). This defaults to the views directory in the application root directory.
view engine, the template engine to use. For example, to use the Pug template engine: app.set('view engine', 'pug').
html
head
title= title
body
h1= message
index.pug
A cryptosystem should be secure even if everything about the system, except the key, is public knowledge. - Kerckhoffs's principle
Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can't break.
Terms (Hash Functions)
Bad Solution #1: plain text password
Solution #2: sha1(password)
Solution #3: sha1(FIXED_SALT + password)
Solution #4: sha1(PER_USER_SALT + password)
for user in users:
PER_USER_SALT = user["salt"]
for password in LIST_OF_COMMON_PASSWORDS:
if sha1(PER_USER_SALT + password) in database_table:
print "Hacker wins! I guessed a password!", password
Salts (alone) Will Not Help You
Double Hashing & Wacky Hash Functions
md5(sha1(password))
md5(md5(salt) + md5(password))
sha1(sha1(password))
sha1(str_rot13(password + salt))
md5(sha1(md5(md5(password) + sha1(password)) + md5(password)))
Solution: bcrypt/pbkdf2 (password)
Security best practices for Express applications in production include:
Use TLS
Use Helmet
Helmet is actually just a collection of smaller middleware functions that set security-related HTTP response headers:
Things to do in your environment / setup (the ops part):
Things to do in your code
Things to do in your code (the dev part):
Don’t use synchronous functions
Do logging correctly
Handle exceptions properly
Use try-catch
app.get('/search', function (req, res) {
// Simulating async operation
setImmediate(function () {
var jsonStr = req.query.params
try {
var jsonObj = JSON.parse(jsonStr)
res.send('Success')
} catch (e) {
res.status(400).send('Invalid JSON string')
}
})
})
Use promises
app.get('/', function (req, res, next) {
// do some sync stuff
queryDb()
.then(function (data) {
// handle data
return makeCsv(data)
})
.then(function (csv) {
// handle csv
})
.catch(next)
})
app.use(function (err, req, res, next) {
// handle error
})
However, there are two caveats:
const wrap = fn => (...args) => fn(...args).catch(args[2])
app.get('/', wrap(async (req, res, next) => {
let company = await getCompanyById(req.query.id)
let stream = getLogoStreamById(company.id)
stream.on('error', next).pipe(res)
}))
Use a load balancer
A load balancer is usually a reverse proxy that orchestrates traffic to and from multiple application instances and servers.