When the buzz about Bun started, I wondered, "Is this just another JS runtime trying to disrupt the JS ecosystem?" I just ignored it and never tried it out. But then the Twitter (call it X, whatever) posts were getting insane everyday and people were liking Bun more and more. I couldn't ignore it at this point, so I hesitantly tried it out. I was a little worried because it was just released back then and could cause some issues. I liked it! Specifically the speed part.
This article will look at speed comparisons of Bun, NPM and PNPM. We will also look at other things that Bun has to offer.
Speed comparison
This section will compare the speeds of installing dependencies for a Vite TypeScript React app. It should be a little heavy, haha. I'm using the following command to create a new Vite application.
bunx create-vite@latest speeds --template react-ts
Feel free to use any package manager to create the project. The dependencies won't be installed; we need to install them manually.
Removing cache
Before proceeding, I want to remove the cache for PNPM and Bun.
pnpm store prune && bun pm cache rm
The above command should remove the cache for both of the package managers. So, all the packages that now get installed are not served through cache, that could provide an unfair advantage to a package manager.
Testing npm install
speed
Use the following command to install dependencies using NPM and note down the time outputted.
time npm install
The time
command helps you get the time it takes for a command to complete execution.
Testing pnpm install
speed
Now, remove the node_modules
folder generated by NPM using the following command (assuming you're on macOS or Linux):
rm -rf node_modules
Use the following command to install dependencies using PNPM and note down the time outputted.
time pnpm install
Testing bun install
speed
Remove the node_modules
folder generated by PNPM. Use the following command to install dependencies using Bun and note down the time outputted.
time bun install
Results are here
The time noted by me and you might vary on many many different factors, but I expect the order to remain the same. Following are my results.
Bun: 6.890s
PNPM: 14.213s
NPM: 36.860s
Bun is the clear winner when it comes to speed! And it's not even close. If you're still using NPM, I don't know what you're waiting for. SWITCH ASAP!
Directly run TypeScript using Bun
Yes, you read the heading right- you can run TS directly with Bun. Okay, for developers who aren't nerds and have a social life, let me give a little context.
If you want to run a TypeScript file in Node.js, you can't directly do that. That's because Node.js doesn't understand TypeScript. It understands JavaScript. To run a "TypeScript" file, you need to transpile it to JavaScript using tsc
and run the JS that's built after the transpilation process.
On the other hand, Bun transpiles the files automatically for you so that you don't need to transpile and run JS each time you make changes. In short, you can pass .ts
files to Bun to run and Bun will not cry about it and do it's job.
You can have a file called as main.ts
and have a simple console.log("Hello World");
in it and run the following command.
bun run main.ts
And the file will run, and you will see Hello World
in your console. Convenient!
Inbuilt watch mode
Picture this: you're working on an Express server made using Node.js and want the changes to reflect immediately without manually restarting the server. What would you do? You would use nodemon!
nodemon main.js
The above command will run main.ts
and watch for changes. If any changes are detected, the file will be run again with updated changes automatically.
What about TypeScript in the Node.js and Express case? You need to install ts-node
the package in your dev dependencies, and you can do the following.
nodemon main.ts
However, there is a problem. If you are relying on fs
and "current directory" by any chance, the current directory in the above case would be the place where the file is run. However, if you transpile the TS into JS, the current directory would be the build
or dist
directory, or whatever you've set in tsconfig.json
file. It can cause a lot of confusion between development and production environments. Of course, you don't want to nodemon inside the production environment. To fix this, you must go fancy by watching the file changes, transpile TS to JS and nodemon-ing the built file.
BUT, what if I told you that when you're using Bun, you need not be bothered about all this at all? We already saw how you can run .ts
directly using Bun. You DO NOT need a build
or dist
directory. Instead, while developing, you can do the following:
bun --watch main.ts
This will
Run the
main.ts
file.Watch for any changes.
If there are any changes, restart the execution.
It's as simple as that. No nodemon, no transpilation, nothing. Since you don't need to transpile anything, you can better know the context of the current directory you will be mentioning in your code.
Cross-platform shell commands
A post by Jarred Sumner on the Bun blog explains why running shell commands is a pain in JavaScript, specifically Node.js. You must do a lot of stuff just to run a simple command in the shell. Well, you can read the blog for more information as to why it's such a pain in Node.js. Here, we will see how easy it is to run shell commands in Bun. Assuming you have a file main.ts
and you want to run the ls
command and log the contents of the command's response; you need the following code.
import { $ } from "bun";
await $`ls`;
That's three lines of codes, or two if we exclude the one empty line. It's that simple. If you want to get the command's response in a variable, the following is the code.
import { $ } from "bun";
const response = await $`ls`.text();
Yes, still a three line code.
But wait. (In Steve Jobs's style) There's ONE MORE THING!
These shell commands are cross-platform. So, if you use a command like rmdir
or basically any native command that is different for Windows and Linux, Bun converts those commands under the hood. So, there is no need to check the platforms and run different commands.
Accessing and writing files using Bun
Bun provides special methods for accessing and writing files.
const file = Bun.file(import.meta.dir + '/package.json'); // BunFile
const pkg = await file.json(); // BunFile extends Blob
pkg.name = 'my-package';
pkg.version = '1.0.0';
await Bun.write(file, JSON.stringify(pkg, null, 2));
In the above code, we are accessing the file using Bun.file()
and converting it into an object and storing it into pkg
. We are then modifying pkg
and writing the updated package.json
using Bun.write()
. It's that easy.
Password hashing using Bun
Hashing and comparing passwords is easy with Bun.
const password = "super-secure-pa$$word";
const hash = await Bun.password.hash(password);
// => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh...
const isMatch = await Bun.password.verify(password, hash);
// => true
No third-party packages are required. Works out of the box. If you want to see how it works when someone inputs the wrong password, check out my video where I talk about it.
Conclusion
In my opinion, Bun is really cool. The last time I checked it on a project that was made using Node, it did have some issues running. But the team at Bun is shipping insanely (of course in a good way) and I think we will have nice things ahead of us. Also, they are working heavily on getting Bun on Windows without using WSL. So, all the Windows users out there, make sure you follow their Twitter account for more information.
I would appreciate any suggestions about my content in the comments.