34

I'm messing around with node and I'm trying to get an instance of the URL class (because of those handy properties). Like:

const { URL } = require('url');
(...)
http.createServer((request,response) => {
    let uri = new URL(request.url);
    (...)
}

But it fails with

TypeError [ERR_INVALID_URL]: Invalid URL: /

It's funny because

const url = require('url');
url.parse();

works. So I got curious about it. I understand this later method is older.

I'm developing locally so to send a request I use localhost:8000 in the browser.

How do I use the information in request to instantiate a new URL object?

Info and things I've looked on already:

node -v
v9.3.0

https://nodejs.org/api/url.html#url_class_url
https://stackoverflow.com/questions/44738065/uncaught-typeerror-url-is-not-a-constructor-using-whatwg-url-object-support-for
https://stackoverflow.com/questions/45047840/in-node-js-how-to-get-construct-url-string-from-whatwg-url-including-user-and-pa
https://stackoverflow.com/questions/47481784/does-url-parse-protect-against-in-a-url
https://stackoverflow.com/questions/17184791/node-js-url-parse-and-pathname-property
Thom
  • 524
  • 1
  • 6
  • 12
  • 2
    The error is telling you the problem. '/' is an invalid URL format. '/' is a path. For it to be a valid URL it needs a host, protocol, and path. – Paul Jan 10 '18 at 22:05

1 Answers1

38

As Joshua Wise pointed out on Github (https://github.com/nodejs/node/issues/12682), the problem is that request.url is NOT an absolute URL. It can be used as a path or relative URL, but it is missing the host and protocol.

Following the API documentation, you can construct a valid URL by supplying a base URL as the second argument. This is still awkward because the protocol is not easily available from request.headers. I simply assumed HTTP:

var baseURL = 'http://' + request.headers.host + '/';
var myURL = new URL(request.url, baseURL);

This is obviously not an ideal solution, but at least you can take advantage of the query string parsing. For example,

URL {
  href: 'http://localhost:8080/?key1=value1&key2=value2',
  origin: 'http://localhost:8080',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost:8080',
  hostname: 'localhost',
  port: '8080',
  pathname: '/',
  search: '?key1=value1&key2=value2',
  searchParams: URLSearchParams { 'key1' => 'value1', 'key2' => 'value2' },
  hash: '' }
tagurit
  • 494
  • 5
  • 13
Jeshurun Hembd
  • 527
  • 7
  • 9
  • I like your solution. We could abstract the baseURL string building to a function that figures out the protocol, perhaps? – Thom Jul 26 '18 at 15:14
  • 1
    How about making the base URL exemplary to express that it is mandatory yet still exemplary? Example: `'example://example.test'`. Notes: Leave the ending slash out to keep parsing the _`pathname`_ from the relative URL. The relative nature of the pathname then can be verified afterwards more easily (first (few) character(s)). `.origin` will be `null` unless the protocol of the relative URL gives an acceptable origin. Benefit is there is no hidden dependency of the _Request_ while it still applies to IANA / RFC conformance, _not_ misleading on the URI scheme (misnamed _`protocol`_ in Node URL). – hakre Feb 06 '21 at 04:31