path.join vs path.resolve
But they are often used in other parts of a Node.js application as well.
Let’s have a look at some examples where we call these methods with the same arguments, so we can directly compare them.
Note that I’m writing this post on a Windows machine and the delimiter will always be
\. Linux and Mac will produce slightly different outputs.
Both methods can accept optional string arguments, as many as you wish. Just for the record, if you don’t provide any argument,
join returns the current directory(
.) and while
resolve also gives back the directory from where the file was executed but it does it as an absolute path from the root:
path.join(); // . path.resolve(); // C:\Users\USERNAME\FULL_PATH_TO_THE_FOLDER
This is a very important difference, which affects how these methods behave in various situations.
If you feed the methods with arguments other than strings, they will throw an error.
Slash or no slash?
/ doesn’t seem to be a big issue but it makes a huge difference. If the first character of the path fragment entered as argument is a
/, an absolute path is immediately created:
path.join('hello'); // hello path.resolve('hello'); // C:\Users\USERNAME\FULL_PATH_TO_THE_FOLDER\hello path.join('/hello'); // \hello path.resolve('/hello'); // C:\hello
join converts the
/ to the delimiter of your operation system.
resolve, on the other hand, always returns an absolute path. With the
/hello the absolute path is created and
resolve attach it to the root directory. It can be the
D or whatever) drive you run the script on Windows and the
home folder on Linux.
Multiple path fragments
Let’s challenge the methods further and call them with multiple path fragments:
path.join('hello', 'path'); // hello\path path.resolve('hello', 'path'); // C:\Users\USERNAME\FULL_PATH_TO_THE_FOLDER\hello\path path.join('/hello', 'path'); // \hello\path path.resolve('/hello', 'path'); // C:\hello\path
This is the same situation as above.
join returns a concatenation of the path fragments and
resolve creates an absolute path.
What if we prepend the
/ to the second path segment?
path.join('hello', '/path'); // hello\path path.resolve('hello', '/path'); // C:\path path.join('/hello', '/path'); // \hello\path path.resolve('/hello', '/path'); // C:\path
join, there’s no change. It diligently joins the path segments and returns the result.
But things are different for
resolve as the
/hello, it doesn’t matter) segment disappeared. This is because
resolve creates paths from right to left and with the
/path an absolute path can be created. The method will stop here and will completely ignore everything to the left.
This can go indefinitely:
path.join('hello', '/path', 'me'); // hello\path\me path.resolve('hello', '/path', 'me'); // C:\path\me path.join('/hello', '/path', '/me'); // \hello\path\me path.resolve('/hello', '/path', '/me'); // C:\me
join concatenates the the path fragments and
resolve looks for the first segment with
/ from the right and append everything up to this point to the root.
Both methods normalize the returned path, which means that they manage the
.. characters the way we normally use them for.
How do they handle it?
.. into the segments:
console.log('join: ', path.join('/..')) // \ console.log('resolve: ', path.resolve('/..')) // C:\
.. means that we have to go up by one level in the folder structure.
In the case of
join the response will be the delimiter itself (
\ on Windows and
/ on Linux and Mac).
resolve, on the other hand, returns an absolute path, so when the first
/ is found from the right (let’s not worry about trailing
/ now), it creates the path from the root.
Add them to paths
Having said that let’s figure out what the responses are if the
.. is added to some path segments:
console.log('join: ', path.join('/hello', '/../path')) // \path console.log('resolve: ', path.resolve('/hello', '/../path')) // C:\path
/hello (or just
hello, it doesn’t matter here) segment will be ignored because we go one level up from
path and this level will be the
hello segment itself. The
/hello (or the first path segment argument as is) and the
/.. will simply cancel out.
join, we step into
hello but then immediately step out of it by going one level higher (
/..), so the result will be
In the case of
path segment will be attached to the root.
console.log('join: ', path.join('hello', '../path')) // path console.log('resolve: ', path.resolve('hello', '../path')) // C:\Users\USERNAME\FULL_PATH_TO_THE_FOLDER\path
The difference is that we have no leading
join returns just
resolve attaches it to the full path from the root. One path segment (
hello in this case) and
.. will again cancel out.
resolve are two frequently used methods when it comes creating paths. The main difference is that
join concatenates (i.e. joins) the path segments and
resolve creates an absolute path from the root. Both methods will normalize the paths i.e. they treat
.. as we normally use them when navigating in the folder structure.
Thanks for reading and see you next time.