Skip to content

Instantly share code, notes, and snippets.

@mikeal
Last active April 21, 2023 17:13
Show Gist options
  • Save mikeal/013dbb2ef3810cfe976cbe16ddb11c6f to your computer and use it in GitHub Desktop.
Save mikeal/013dbb2ef3810cfe976cbe16ddb11c6f to your computer and use it in GitHub Desktop.
HTTP Client Comparison
const r2 = require('r2')
let doJsonThing = async (path, propname) => {
let res = await r2(`http://api.com${path}`).json
return res[propname]
}
const request = require('request')
let doJsonThing = (path, propname, cb) => {
request(`http://api.com${path}`, {json: true}, (err, resp, body) => {
if (err) return cb(err)
if (resp.statusCode < 200 || resp.statusCode > 299) {
return cb(new Error(`Status not 200, ${resp.statusCode}`))
}
cb(null, body[propname])
}
}
@bahmutov
Copy link

Agree with @bluepnume and want to point out that when using promises, getting property from returned JSON object is extra thing better left to functional library like Ramda. Also make a module that only gets JSON - since it is such a common case

const requestJson = require('request-json')
const R = require('ramda')
requestJson('http...').then(R.prop('user'))

@mikeal
Copy link
Author

mikeal commented Aug 31, 2017

So, I've been writing that 2xx checking boilerplate for the better part of 7 years in different projects. If there was an easy way to add it request, I would have done so a long time ago.

Here's the problem, you have to signal to the API that you want JSON support (which adds accept headers). This also sets up the JSON decoding. However, a fair number of APIs return valid JSON bodies for their error conditions.

Because there's just a single handler (the callback) for socket errors, response errors, and success we can't add default status code checking for this case in request itself without blocking people from being able to handle their own http errors codes that have json bodies. It has been considered several times.

The reason you can do this w/ the promise API is that you have multiple entry points for the success condition. You can still support people checking their own 4xx errors by doing await r2(url).response but when doing await r2(url).json you can add some additional default semantics that make sense 99% of the time.

The real power here isn't anything as simple as callbacks vs. promises. The real power is in the fact that errors throw, and that you can customize what is considered an error based on what property is accessed. This allows you to add all kinds of semantics for different usages that you just can't inspect when all you have is a callback to handle every class of error and success.

You don't need users to plug and propagate errors by hand, that's all language level now. And because you aren't passing in this future handler for the error you can create multiple entry points for the success conditions.

@jakearchibald
Copy link

jakearchibald commented Aug 31, 2017

@mikeal there's also response.ok which maps to what you wrote. https://fetch.spec.whatwg.org/#ref-for-dom-response-ok①.

Wait I'm being an idiot, that was the old example. Ignore me.

@darrentorpey
Copy link

Thanks for the detailed explanation, @mikeal -- interesting PoC

@mrpeu
Copy link

mrpeu commented Sep 1, 2017

I want to believe you; I feel like there is something powerful I'm missing. But I don't understand your answer to @bluepnume and @bahmutov.
@jakearchibald: I'll take over the idiot badge ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment