URL.canParse()

Reports whether a string can be parsed as a URL, returning a boolean instead of throwing.

Signature
URL.canParse(input[, base])
  • Added in Node.js v19.9.0, v18.17.0

Parameters

ParameterTypeDescription
$inputstringThe absolute or relative input URL to parse. If input is relative, then base is required. If input is absolute, the base is ignored. If input is not a string, it is converted to a string first.
$baseoptionalstringThe base URL to resolve against if the input is not absolute. If base is not a string, it is converted to a string first.

Return value

booleantrue if input, resolved against base, parses to a valid URL; false otherwise.

Errors

Does not throw for unparseable input; that is the point of the method.

URL.canParse() is a static method that answers one question — would new URL(input, base) succeed? — without the cost and noise of an exception. It applies the identical WHATWG parsing rules, so a true result guarantees the constructor will not throw on the same arguments.

When to reach for it

Reach for it when validating untrusted input in bulk: filtering a list of submitted links, guarding a config value, or short-circuiting a request handler before doing real work. Throwing and catching on a hot path is measurably slower than a boolean check, and a try/catch around a constructor also swallows unrelated errors written inside the same block.

What trips people up

It validates syntax, not reachability or policy. URL.canParse('mailto:[email protected]') is true, and so is any string with a well-formed scheme — including javascript: and file:. If you need an HTTPS web address specifically, you still have to construct the URL and inspect protocol.

The second argument changes the answer entirely. With a base, relative strings such as '/a?b=1' and even '' become parseable, so passing a base makes validation much more permissive than you may intend.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Filter a user-submitted list of links down to the ones that actually parse
  • Reject a malformed webhook or callback URL at request validation time
  • Decide whether a config string is a URL or a bare hostname before normalizing it
  • Avoid try/catch overhead when scanning thousands of strings for links

Frequently asked questions

What is the difference between URL.canParse and wrapping new URL in try/catch?

They give the same verdict, but canParse returns a boolean instead of constructing and throwing, which is faster on hot paths and keeps unrelated errors in the block from being swallowed. Use canParse to validate, then construct once if you actually need the parsed object.

Which Node version added URL.canParse?

It was added in Node v19.9.0 and backported to v18.17.0. On older releases you need the try/catch form, or a small helper that wraps new URL().

Does URL.canParse check that the site exists?

No. It is a pure syntax check with no DNS lookup and no request — a URL for a domain that was never registered still returns true.

More examples

Validating that a link is a usable web URL

canParse alone accepts javascript: and ftp: because both are syntactically valid URLs. The extra protocol check is what makes this suitable for deciding whether a string is safe to render as a link or pass to fetch.

Example · javascript
function isWebUrl(value) {
  if (!URL.canParse(value)) return false;
  const { protocol } = new URL(value);
  return protocol === 'https:' || protocol === 'http:';
}

console.log(isWebUrl('https://example.com/docs'));
console.log(isWebUrl('http://localhost:3000'));
console.log(isWebUrl('javascript:alert(1)'));
console.log(isWebUrl('ftp://files.example.com'));
console.log(isWebUrl('not a url'));

Splitting an input list into valid and invalid

'htp:/broken' is rejected because there is no host after a special-looking scheme, and 'http://' fails because http is a special scheme that requires a non-empty host. Only the two survivors are re-parsed, so the constructor is never given something that would throw.

Example · javascript
const input = ['https://a.example', 'htp:/broken', 'https://b.example/x?y=1', 'http://'];

const valid = input.filter((s) => URL.canParse(s));
const invalid = input.filter((s) => !URL.canParse(s));

console.log('valid:', valid);
console.log('invalid:', invalid);
console.log('hosts:', valid.map((s) => new URL(s).hostname));

Relative paths need a base to validate

Without a base every relative reference would report false. With one, they resolve like links on a page: '../shared/app.css' climbs out of /assets/, while an absolute input ignores the base entirely.

Example · javascript
const base = 'https://cdn.example.com/assets/';
const refs = ['logo.svg', '../shared/app.css', 'https://other.example/x', '///'];

for (const ref of refs) {
  const ok = URL.canParse(ref, base);
  console.log(ref, '->', ok, ok ? new URL(ref, base).href : '(unparseable)');
}

Discussion

  • Be the first to comment on this lesson.