Reputation: 11
This is my first question on stackoverflow, so please be gentle. I'm building a simple app using node.js that relies on API calls to an external geo-encoding service. My program runs locally, but fails when deployed to heroku. Here's the function that is failing:
async function geoEncode(restaurant = false, string = false) {
let address;
address = (string) ? string : require('querystring').escape(`${restaurant.street_address}, ${restaurant.city_state_zip}, ${restaurant.state}, ${restaurant.zip}`);
let url = await geoApi.replace("SEARCH_STRING", address);
let encodedURL = new URL(url);
let init = {};
let headers = {
"async": true,
"crossDomain": true,
"method": "GET",
"Host": "us1.locationiq.com",
}
init.headers = headers;
try {
let response = await fetch(encodedURL.href, init);
let output = await response.json();
return output;
} catch(err){
console.log("this log an error in geoEncode", err);
}
}
I recognize that this code is currently a mess of unnecessary encoding and redundant headers. Side-effects of my failed debugging process. When run locally, the program sends a valid url to the service and returns responsive data. When deployed on heroku,
let encodedURL = new URL(url);
throws
TypeError [ERR_INVALID_URL]: Invalid URL: "https://us1.locationiq.com/v1/search.php?key ...
I have edited this url for my question to protect the API key, but when I take that string (minus the quotes) and run it through my browser it returns valid JSON.
Before deployment I wasn't doing any encoding on my urls at all - I just built a string and fed it to the node-fetch module's fetch method. It errored on Heroku, with the message "TypeError: Only absolute URLs are supported". This error message, I have since learned, means that node-fetch's request method is failing to parse either a protocol or a hostname. I have managed to determine that when deployed on heroku, it fails to parse both.
The obvious difference between my local environment and heroku's is that I have set my environmental variables through heroku's dashboard rather than through my .env file. Because I'm a little unclear about how that works most of my recent debugging has been trying to ensure that the string I'm feeding to the fetch API is in a consistent, uniform encoding. So I have pre-escaped my code, manually utf8 encoded it, run it through the URL module, and fed the output of that url-encoding into the function without touching it. It still works locally and fails remotely.
My basic stack is MongoDB, Express, Node, Bootstrap. Calls I make to my cloud MongoDB server work, as do calls to the API from which I source the data I geo-encode. Only these calls to my geo-encoding service are failing, so the environment variables I use for my MongoDB connection are functioning as expected.
Thanks for your help!
Edit 1:
In answer to a comment below (sorry about the formatting of my attempted comment-answer; I now know that markdown formatting is limited in comments), the errors I posted are logged from heroku using the command "heroku logs --tail". I have manually logged the URL I am trying to encode (what follows is an edited example):
URL to be encoded: "https://us1.locationiq.com/v1/search.php?key=KEY-REDACTED&format=json&q=101%20NICKERSON%20ST%2C%20SEATTLE%2C%20WA%2C%2098109-1654"
as well as the error heroku is reporting:
TypeError [ERR_INVALID_URL]: Invalid URL: "https://us1.locationiq.com/v1/search.php?key=KEY-REDACTED=json&q=101%20NICKERSON%20ST%2C%20SEATTLE%2C%20WA%2C%2098109-1654"
Edit 2:
I think I may have not provided enough error logging in my post. Here is a more complete error log. I hope this is helpful to someone who can in turn help me.
2019-11-14T23:35:09.646585+00:00 app[web.1]: (node:23) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_URL]: Invalid URL: "https://us1.locationiq.com/v1/search.php?key=KEY_REDACTED&format=json&q=101%20NICKERSON%20ST%2C%20SEATTLE%2C%20WA%2C%2098109-1654"
2019-11-14T23:35:09.646591+00:00 app[web.1]: at onParseError (internal/url.js:243:9)
2019-11-14T23:35:09.646593+00:00 app[web.1]: at new URL (internal/url.js:319:5)
2019-11-14T23:35:09.646595+00:00 app[web.1]: at geoEncode (/app/server.js:308:20)
2019-11-14T23:35:09.646599+00:00 app[web.1]: at processTicksAndRejections (internal/process/task_queues.js:93:5)
2019-11-14T23:35:09.646602+00:00 app[web.1]: at async Timeout._onTimeout (/app/server.js:373:29)
Upvotes: 1
Views: 5505
Reputation: 11
Resolved! The answer turns out to be predictably dull, but here it is:
When I entered the environmental variable referenced as geoAPI in the heroku dashboard, I entered it as a string with quotes. Heroku must already type environmental variables as strings, because the result was a string beginning and ending with "", which was causing the url module to fail to parse. I eventually discovered the problem when I console logged the output of querystring.parse(url) and received:
parsed querystring: [Object: null prototype] {
2019-11-19T21:54:59.724380+00:00 app[web.1]: '"https://us1.locationiq.com/v1/search.php?key': 'REDACTED',
2019-11-19T21:54:59.724382+00:00 app[web.1]: format: 'json',
2019-11-19T21:54:59.724385+00:00 app[web.1]: q: '101 NICKERSON ST, SEATTLE, WA, 98109-1654"' }
The extra quotes made everything up to the first '=' read as a key, apparently. And that's how I spotted the extra quotes. Removing the extra quotes resolved the problem. Glad it's over, but sad that it turned out to be just a formatting error in the heroku dashboard.
My small lesson from this: Don't put you heroku environmental variables in quotes. That's not how they work.
My big lesson from this: When something is mysterious, check your environment early. And if you check it, don't find anything, and stay stuck on the problem for a long time, check it again.
Upvotes: 0