I am building a rest API using undici
.
I am getting the following error:
TypeError: fetch failed
I have implemented the following code:
const crypto = require('crypto');
const OAuth = require('oauth-1.0a');
const OAuth2 = require('../utils/OAuth2');
const undici = require('undici');
const { cloneDeep, isNil } = require('lodash');
const { log } = require('../config/log');
const { rest } = require('../config/vars');
module.exports.request = async (clientName, method, path, options) => {
// eslint-disable-next-line security/detect-object-injection
const config = rest[clientName];
console.log(config);
let preparedOptions = await prepareOptions(clientName, method, config.base.prefixUrl + path, options, true);
console.log(preparedOptions);
const res = await undici.fetch(config.base.prefixUrl, preparedOptions)
.catch(async (err) => {
if (!isNil(err.response) && (err.response.statusCode === 401 || err.response.statusCode === 403)) {
preparedOptions = await prepareOptions(clientName, method, config.base.prefixUrl + path, options, true);
return undici.request(config.base.prefixUrl, preparedOptions);
}
throw err;
});
return res;
};
module.exports.get = async (clientName, path, options = {}) => {
return module.exports.request(clientName, 'GET', path, options);
};
module.exports.post = async (clientName, path, body, options = {}) => {
options.body = body;
return module.exports.request(clientName, 'POST', path, options);
};
async function prepareOptions(clientName, method, url, options, forceRefresh = false) {
const clientConfig = rest[clientName]; // eslint-disable-line security/detect-object-injection
const defaultOption = isNil(options) ? {} : cloneDeep(options);
const prepareOption = Object.assign(defaultOption, clientConfig.option);
const config = rest[clientName]; // eslint-disable-line security/detect-object-injection
prepareOption.method = method;
if (!isNil(config.OAuth2)) {
const token = await OAuth2.getToken(clientName, config.OAuth2, forceRefresh);
if (isNil(prepareOption['headers'])) { // eslint-disable-line dot-notation
prepareOption['headers'] = {}; // eslint-disable-line dot-notation
}
prepareOption['headers'].authorization = `Bearer ${ token }`; // eslint-disable-line dot-notation
} else if (!isNil(config.OAuth1)) {
const oauth = OAuth({
consumer: {
key: config.OAuth1.key,
secret: config.OAuth1.secret,
},
signature_method: config.OAuth1.signatureMethod, // 'HMAC-SHA1',
hash_function: (baseString, key) => {
return crypto.createHmac(getHmacAlgorithm(config.OAuth1.signatureMethod), key)
.update(baseString)
.digest('base64');
},
});
prepareOption['headers'] = oauth.toHeader(oauth.authorize({ // eslint-disable-line dot-notation
url,
method,
}));
} else if (!isNil(config.BasicAuth)) {
const basicAuth = Buffer.from(`${ config.BasicAuth.username }:${ config.BasicAuth.password }`)
.toString('base64');
if (isNil(prepareOption['headers'])) { // eslint-disable-line dot-notation
prepareOption['headers'] = {}; // eslint-disable-line dot-notation
}
prepareOption['headers'].authorization = `Basic ${ basicAuth }`; // eslint-disable-line dot-notation
}
return prepareOption;
}
I have implemented the following test:
const APIError = require('../utils/APIError');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const { MockAgent, setGlobalDispatcher } = require('undici');
chai.use(chaiAsPromised);
const { assert } = chai; // eslint-disable-line no-shadow
const agent = new MockAgent();
agent.disableNetConnect();
setGlobalDispatcher(agent);
describe('REST', () => {
after(() => {
agent.close();
});
describe('REST pass', () => {
it('200', async () => {
const mockAgent = agent.get('https://test.ca');
mockAgent
.intercept({
path: '/test',
method: 'GET',
})
.reply(200, {
data: 'test',
});
const result = await rest.get('test', 'test');
// assert.equal(result.statusCode, 200);
assert.deepEqual(result.body, { data: 'test' });
agent.assertNoPendingInterceptors();
});
});
});
console.log(config)
{
base: { prefixUrl: 'https://test.ca'}, option: {path: '/'}
}
console.log(preparedOptions)
{ path: '/', method: 'GET' }
I am fairly new using undici
, and I am not sure why this error is occurring, I would appreciate any input.
How can I overcome this error?
You would have to update your request
function
module.exports.request = async (clientName, method, path, options) => {
// eslint-disable-next-line security/detect-object-injection
const config = rest[clientName];
console.log(config);
let preparedOptions = await prepareOptions(clientName, method, config.base.prefixUrl + path, options, true);
console.log(preparedOptions);
console.log(config.base.prefixUrl + path);
return undici.fetch(config.base.prefixUrl + path, { body: options.data, method })
.then(async (res) => {
return { statusCode: res.status, body: await res.json() }; // maybe text()
})
.catch(async (err) => {
if (!isNil(err.response) && (err.response.statusCode === 401 || err.response.statusCode === 403)) {
preparedOptions = await prepareOptions(clientName, method, config.base.prefixUrl + path, options, true);
return undici.fetch(config.base.prefixUrl);
}
throw err;
});
};
And also your tests to
it('200 - undici', async () => {
const mockAgent = agent.get('https://test.ca');
mockAgent
.intercept({
path: '/test',
method: 'GET',
})
.reply(200, {
data: 'test',
});
const result = await rest.get('test', '/test');
assert.equal(result.statusCode, 200);
assert.deepEqual(result.body, { data: 'test' });
agent.assertNoPendingInterceptors();
});