Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use require("path").join to safely concatenate urls?

People also ask

Why do we use path join?

The path. join() method is used to join a number of path-segments using the platform-specific delimiter to form a single path.

What is path join Nodejs?

The path. join() method joins the specified path segments into one path. You can specify as many path segments as you like. The specified path segments must be strings, separated by comma.


No. path.join() will return incorrect values when used with URLs.

It sounds like you want new URL(). From the WHATWG URL Standard:

new URL('/one', 'http://example.com/').href    // 'http://example.com/one'
new URL('/two', 'http://example.com/one').href // 'http://example.com/two'

Note that url.resolve is now marked as deprecated in the Node docs.

As Andreas correctly points out in a comment, url.resolve (also deprecated) would only help if the problem is as simple as the example. url.parse also applies to this question because it returns consistently and predictably formatted fields via the URL object that reduces the need for "code full of ifs". However, new URL() is also the replacement for url.parse.


No, you should not use path.join() to join URL elements.

There's a package for doing that now. So rather than reinvent the wheel, write all your own tests, find bugs, fix them, write more tests, find an edge case where it doesn't work, etc., you could use this package.

url-join

https://github.com/jfromaniello/url-join

Install

npm install url-join

Usage

var urljoin = require('url-join');

var fullUrl = urljoin('http://www.google.com', 'a', '/b/cd', '?foo=123');

console.log(fullUrl);

Prints:

'http://www.google.com/a/b/cd?foo=123'


Axios has a helper function that can combine URLs.

function combineURLs(baseURL, relativeURL) {
  return relativeURL
    ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
    : baseURL;
}

Source: https://github.com/axios/axios/blob/fe7d09bb08fa1c0e414956b7fc760c80459b0a43/lib/helpers/combineURLs.js


This can be accomplished by a combination of Node's path and URL:

  1. Require the packages:
const nodeUrl = require('url')
const nodePath = require('path')
  1. Start by making a URL object to work with:
> const myUrl = new nodeUrl.URL('https://example.com')
  1. Use pathname= and path.join to construct any possible combination:
> myUrl.pathname = nodePath.join('/search', 'for', '/something/')
'/search/for/something/'

(you can see how liberal path.join is with arguments)

  1. At this point your URL reflects the ultimate desired result:
> myUrl.toString()
'https://example.com/search/for/something/'

Why this approach?

This technique uses built-in libraries. The less third-party dependencies the better, when it comes to CVEs, maintenance, etc.

Nothing will be more proven or better tested than standard libs.

PS: Never manipulate URLs as strings!

When I review code I'm adamant about never manipulating URLs as strings manually. For one, look how complicated the spec is.

Secondly, the absence/presence of a trailing/prefixed slash (/) should not cause everything to break! You should never do:

const url = `${baseUrl}/${somePath}`

and especially not:

uri: host + '/' + SAT_SERVICE + '/' + CONSTELLATION + '/',

Of which I have seen.


The WHATWG URL object constructor has a (input, base) version, and the input can be relative using /, ./, ../. Combine this with path.posix.join and you can do anything:

const {posix} = require ("path");
const withSlash = new URL("https://example.com:8443/something/");
new URL(posix.join("a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/something/a/b/c'
new URL(posix.join("./a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/something/a/b/c'
new URL(posix.join("/a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/a/b/c'
new URL(posix.join("../a", "b", "c"), withSlash).toString(); // 'https://example.com:8443/a/b/c'
const noSlash = new URL("https://example.com:8443/something");
new URL(posix.join("./a", "b", "c"), noSlash).toString(); // 'https://example.com:8443/a/b/c'

When I tried PATH for concatenating url parts I run into problems. PATH.join stripes '//' down to '/' and this way invalidates an absolute url (eg. http://... -> http:/...). For me a quick fix was:

baseurl.replace(/\/$/,"") + '/' + path.replace(/^\//,"") )

or with the solution posted by Colonel Panic:

[pathA.replace(/^\/|\/$/g,""),pathB.replace(/^\/|\/$/g,"")].join("/")