20 Jan, 2017
Here are some useful snippits of code for working with Node.js
This is a function that can be used to get JSON from a URL. It supports a timeout.
const url = require('url')
function fetchJson (spec) {
const {get, log, url: inputUrl, method, headers, timeout, allowedStatusCodes = [200]} = spec
let runningTimeout;
return new Promise((resolve, reject) => {
if (method && (method !== 'GET')) {
reject({msg: 'fetch.fetchJson: Only GET is supported'})
}
const options = Object.assign(url.parse(inputUrl, true), {headers})
log(`Making a GET request to ${inputUrl}`)
const request = get(options, (response) => {
let body = ''
if (allowedStatusCodes && (allowedStatusCodes.indexOf(response.statusCode) === -1)) {
request.abort()
reject({msg: `fetch.fetchJson: unexpected response status code ${response.statusCode}`, detail: allowedStatusCodes})
}
response.on('data', (d) => {
body += d
})
response.on('end', () => {
if (runningTimeout) {
clearTimeout(runningTimeout)
}
try {
resolve(JSON.parse(body))
} catch (error) {
reject({msg: 'fetch.fetchJson: parsing error', detail: error, body: body})
}
})
response.on('error', (error) => {
if (runningTimeout) {
clearTimeout(runningTimeout)
}
reject({msg: 'fetch.fetchJson response error', detail: error, body: body})
})
})
request.on('error', (error) => {
if (runningTimeout) {
clearTimeout(runningTimeout)
}
reject({msg: 'fetch.fetchJson request error', error: error})
})
if (timeout) {
runningTimeout = setTimeout(() => {
request.abort()
reject({msg: `fetch.fetchJson timed out after ${timeout}ms`, error: 'Timeout'})
}, timeout)
}
})
}
Used like this:
function log (...args){
console.log(...args)
}
const https = require('https')
fetchJson({
url: 'https://jimmyg.org/some/json.json',
timeout: 1000,
log,
get: https.get,
method: 'GET',
allowedStatusCodes: [200, 404],
headers: {},
}).then((result) => {
log('SUCCESS', JSON.stringify(result, null, 4))
}).catch((error) => {
log('FAIL', JSON.stringify(error, null, 4))
})
This gives the following error because the URL doesn't exist.
Making a GET request to https://jimmyg.org/some/json.json
FAIL {
"msg": "fetch.fetchJson: parsing error",
"detail": {},
"body": "<html>\n<head><title>James Gardner</title></head>\n<body>\n\n<h1>Not Found</h1>\n\n<p>Perhaps that page is still being migrated. If you think the page should exist, please drop me a line at james at pythonweb.org.</p>\n</body>\n</html>"
}
For an insecure version, use http.get
.
This line is really useful:
JSON.stringify(result, null, 4);
It produces JSON indented 4 spaces.
You can make the usage less verbose with the help of withDefaults()
.
You sometimes run into a situation where it is useful to break code into smaller parts but have each of those dependencies share access to an expensive resource.
const assert = require('assert')
function once (target, spec) {
let called = false
let lastSpec
if (called !== false) {
assert.deepStrictEqual(spec, lastSpec, 'Called with different params')
}
return function () {
if (called === false) {
called = target(spec)
lastSpec = spec
}
return called
}
}
I also like to install standard
and nyc
globally so that I can use them on scripts like the above:
sudo npm install -g standard nyc
Then:
standard --fix s.js
nyc node s.js
The coverage output might look like this:
[ 1, 2 ]
[ { ok: false, error: 1 }, { ok: true, result: 2 } ]
----------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
s.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|----------------|
const fs = require('fs')
function readFile (spec) {
const {path, encoding = 'utf8'} = spec
return new Promise((resolve, reject) => {
try {
fs.readFile(path, encoding, (error, data) => {
if (error) {
reject({error})
} else {
resolve(data)
}
})
} catch (error) {
reject({error})
}
})
}
module.exports = readFile
Node also comes with an assert module so instead of running tests using a runner, you can also just write a test program that imports your module, executes code and makes assertions. I like this style of testing without frameworks.
You can always print to the terminal in different colours like this:
console.log('\x1b[32m%s\x1b[0m', 'green');
console.log('\x1b[31m%s\x1b[0m', 'red');
console.log('\x1b[33m%s\x1b[0m', 'yellow');
console.log('\x1b[37m%s\x1b[0m', 'light grey');
console.log('\x1b[90m%s\x1b[0m', 'dark grey');
Or print Unicode characters like this:
console.log('✗')
console.log('✓')
Or combine both!
console.log('\x1b[32m%s\x1b[0m', '✓');
console.log('\x1b[31m%s\x1b[0m', '✗');
Copyright James Gardner 1996-2020 All Rights Reserved. Admin.