Modern Node.js Patterns
kashw1n.com
kashw1n.com
# Run with restricted file system access
node --experimental-permission \
--allow-fs-read=./data --allow-fs-write=./logs app.js
# Network restrictions
node --experimental-permission \
--allow-net=api.example.com app.js
Looks like they were inspired by Deno. That's an excellent feature. https://docs.deno.com/runtime/fundamentals/security/#permiss...The "proper" place to solve this, is in the OS. Where it has been solved, including all the inevitable corner cases, already.
Why reinvent this wheel, adding complexity, bug-surface, maintenance burden and whatnot to your project? What problem dies it solve that hasn't been solved by other people?
Deployments that need to configure OSes in a particular way are difficult (the existence of docker, kubernetes, snap are symptoms of this difficulty). It requires a high level of privilege to do so. Upgrades and rollbacks are challenging, if ever done. OSes sometimes don't provide solution when we go beyond one hardware.
If "npm start" can restrain the permissions to what it should be for the given version of the code, I will use it and I'll be happy.
Do One Thing (and do it well).
A special domain specific scheduler microservice? One of the many Cron replacements? One of the many "SaaS cron"? Systemd?
This problem has been solved. Corner cases ironed out. Free to use.
Same for ENV var as configurations (as opposed to inventing yet another config solution), file permissions, monitoring, networking, sandboxing, chrooting etc. the amount of broken, insecure or just inefficient DIY versions of stuff handled in an OS I've had to work around is mind boggling. Causing a trice a loss: the time taken to build it. That time not spent on the business domain, and the time to them maintain and debug it for the next fifteen years.
[0] https://www.karltarvas.com/macos-app-sandboxing-via-sandbox-...
Also modern software security is really taking a look at strengthening software against supply chain vulnerabilities. That looks less like traditional OS and more like a capabilities model where you start with a set of limited permissions and even within the same address space it’s difficult to obtain a new permission unless your explicitly given a handle to it (arguably that’s how all permissions should work top to bottom).
The problem with the "solutions" s.a. the one in Node.js is that Node.js doesn't get to decide how eg. domain names are resolved. So, it's easy to fool it to allow or to deny access to something the author didn't intend for it.
Historically, we (the computer users) decided that operating system is responsible for domain name resolution. It's possible that today it does that poorly, but, in principle we want the world to be such that OS takes care of DNS, not individual programs. From administrator perspective, it spares the administrator the need to learn the capabilities, the limitations and the syntax of every program that wants to do something like that.
It's actually very similar thing with logs. From administrator perspective, logs should always go to stderr. Programs that try to circumvent this rule and put them in separate files / send them into sockets etc. are a real sore spot of any administrator who'd spent some times doing his/her job.
Same thing with namespacing. Just let Linux do its job. No need for this duplication in individual programs / runtimes.
I dunno how GP would do it, but I run a service (web app written in Go) under a specific user and lock-down what that user can read and write on the FS.
For networking, though, that's a different issue.
Meaning Windows? It also has file system permissons on an OS level that are well-tested and reliable.
> not all Node developers know or want to know much about the underlying operating system
Thing is, they are likely to not feel up for understanding this feature either, nor write their code to play well with it.
And if they at some point do want to take system permissions seriously, they'll find it infinitely easier to work with the OS.
nothing. Except for "portability" arguments perhaps.
Java has had security managers and access restrictions built in but it never worked very well (and is quite cumbersome to use in practice). And there's been lots of bypasses over the years, and patch work fixes etc.
Tbh, the OS is the only real security you could trust, as it's as low a level as any application would typically go (unless you end up in driver/kernal space, like those anti-virus/anti-cheat/crowdstrike apps).
But platform vendors always want to NIH and make their platform slightly easier and still present the similar level of security.
This is my thought on using dotenv libraries. The app shouldn’t have to load environment variables, only read them. Using a dotenv function/plugin like in omz is far more preferable.
The argument often heard though is 'but windows'. Though if windows lacks env (or Cron, or chroot, etc) the solution would be to either move to an env that does support it, or introduce some tooling only for the windows users.
Not build a complex, hierarchical directory scanner that finds and merges all sorts of .env .env.local and whatnots.
On dev I often do use .ENV files, but use zenv or a loadenv tool or script outside of the projects codebase to then load these files into the env.
I've been trying to figure out a good way to do this for my Python projects for a couple of years now. I don't yet trust any of the solutions I've come up with - they are inconsistent with each other and feel very ironed to me making mistakes due to their inherent complexity and lack of documentation that I trust.
For a solution to be truly generic to OS, it's likely better done at the network level. Like by putting your traffic through a proxy that only allows traffic to certain whitelisted / blacklisted destinations.
Whilst this is (effectively) an Argument From Authority, what makes you assume the Node team haven't considered this? They're famously conservative about implementing anything that adds indirection or layers. And they're very *nix focused.
I am pretty sure they've considered "I could just run this script under a different user"
(I would assume it's there because the Permissions API covers many resources and side effects, some of which would be difficult to reproduce across OSes, but I don't have the original proposal to look at and verify)
I often hear similar arguments for or against database level security rules. Row level security, for example, is a really powerful feature and in my opinion is worth using when you can. Using RLS doesn't mean you skip checking authorization rules at the API level though, you check on author in your business logic _and_ in the database.
If you don't know what DNS search path is, here's my informal explanation: your application may request to connect to foo.bar.com or to bar.com, and if your /etc/resolv.conf contains "search foo", then these two requests are the same request.
This is an important feature of corporate networks because it allows macro administrative actions, temporary failover solutions etc. But, if a program is configured with Node.js without understanding this feature, none of these operations will be possible.
From my perspective, as someone who has to perform ops / administrative tasks, I would hate it if someone used these Node.js features. They would get in the way and cause problems because they are toys, not a real thing. Application cannot deal with DNS in a non-toy way. It's the task for the system.
For example, the problem of "one micro service won't connect to another" was traditionally an ops / environments / SRE problem. But now the app development team has to get involved, just in case someone's used one of these new restrictions. Or those other teams need to learn about node.
This is non consensual devops being forced upon us, where everyone has to learn everything.
This leads to the node js teams to have to learn DevOps anyway because the DevOps teams do a subpar job with it otherwise.
Same with doing frontend builds and such. In other languages I’ve noticed (particularly Java / Kotlin) DevOps teams maintain the build tools and configurations around it for the most part. The same has not been true for the node ecosystem, whether it’s backend or Frontend
If an existing feature is used too little, then I'm not sure if rebuilding it elsewhere is the proper solution. Unless the existing feature is in a fundamentally wrong place. Which this isn't: the OS is probably the only right place for access permissions.
An obvious solution would be education. Teach people how to use docker mounts right. How to use chroot. How Linux' chmod and chown work. Or provide modern and usable alternatives to those.
Also, I'd bet my monthly salary on that Node.js implementation of this feature doesn't take into account multiple possible corner cases and configurations that are possible on the system level. In particular, I'd be concerned about DNS search path, which I think would be hard to get right in userspace application. Also, what happens with /etc/hosts?
From administrator perspective I don't want applications to add another (broken) level of manipulating of discovery protocol. It usually very time consuming and labor intensive task to figure out why two applications which are meant to connect aren't. If you keep randomly adding more variables to this problem, you are guaranteed to have a bad time.
That's a cool feature. Using jlink for creating custom JVMs does something similar.
That's a good feature. What you are saying is still true though, using the OS for that is the way to go.
PHP used to have (actually, still has) an "open_basedir" setting to restrict where a script could read or write, but people found out a number of ways to bypass that using symlinks and other shenanigans. It took a while for the devs to fix the known loopholes. Looks like node has been going through a similar process in the last couple of years.
Similarly, I won't be surprised if someone can use DNS tricks to bypass --allow-net restrictions in some way. Probably not worth a vulnerability in its own right, but it could be used as one of the steps in a targeted attack. So don't trust it too much, and always practice defense in depth!
In both Java and .NET VMs today, this entire facility is deprecated because they couldn't make it secure enough.
The whole idea of a hierarchical directory structure is an illusion. There can be all sorts of cross-links and even circular references.
I don't understand this sort of complaint. Would you prefer that they didn't worked on this support ever? Exactly what's your point? Airing trust issues?
Curious what other folks think and if there are any other options? I feel like I've searched pretty exhaustively, and it's the only one I found that was both lightweight and had robust enough type safety.
I think `ts-rest` is a great library, but the lack of maintenance didn't make me feel confident to invest, even if I wasn't using express. Have you ever considered building your own in-house solution? I wouldn't necessarily recommend this if you already have `ts-rest` setup and are happy with it, but rebuilding custom versions of 3rd party dependencies actually feels more feasible nowadays thanks to LLMs. I ended up building a stripped down version of `ts-rest` and am quite happy with it. Having full control/understanding of the internals feels very good and it surprisingly only took a few days. Claude helped immensely and filled a looot of knowledge gaps, namely with complicated Typescript types. I would also watch out for treeshaking and accidental client zod imports if you decide to go down this route.
I'm still a bit in shock that I was even able to do this, but yeah building something in-house is definitely a viable option in 2025.
Luckily, oRPC had progressed enough to be viable now. I cannot recommend it over ts-rest enough. It's essentially tRPC but with support for ts-rest style contracts that enable standard OpenAPI REST endpoints.
Of course I'd rather not maintain my own fork of something that always should have been part of poi, but this was better than maintaining an impossible mix of dependencies.
I would say this oversight was a blessing in disguise though, I really do appreciate minimizing dependencies. If I could go back in time knowing what I know now, I still would've gone down the same path.
[1] https://hono.dev/docs/guides/validation#zod-validator-middle...
For prototypes I'll sometimes reach for tRPC. I don't like the level of magic it adds for a production app, but it is really quick to prototype with and we all just use RPC calls anyway.
For procudtion I'm most comfortable with zod, but there are quite a few good options. I'll have a fetchApi or similar wrapper call that takes in the schema + fetch() params and validates the response.
async function fetchDataWithAxios() {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
console.log('Axios Data:', response.data);
} catch (error) {
console.error('Axios Error:', error);
}
}
async function fetchDataWithFetch() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!response.ok) { // Check if the HTTP status is in the 200-299 range
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json(); // Parse the JSON response
console.log('Fetch Data:', data);
} catch (error) {
console.error('Fetch Error:', error);
}
}The following seems cleaner than either of your examples. But I'm sure I've missed the point.
fetch(url).then(r=>r.ok ? r.json() : Promise.reject(r.status))
.then(
j=>console.log('Fetch Data:', j),
e=>console.log('Fetch Error:', e)
);
I share this at the risk of embarrassing myself in the hope of being educated.So treating "get a response" and "get data from a response" separately works out well for us.
const data = (await fetch(url)).then(r => r.json())
But it's very easy obviously to wrap the syntax into whatever ergonomics you like.[1] convenient capability - otherwise you'd use XMLHttpRequest
2. You don't need to use axios. The main value was that it provides a unified API that could be used across runtimes and has many convenient abstractions. There were plenty of other lightweight HTTP libs that were more convenient than the stdlib 'http' module.
You can obviously do that with fetch but it is more fragmented and more boilerplate
The fetch() change has been big only for the libraries that did need HTTP requests, otherwise it hasn't been such a huge change. Even in those it's been mostly removing some dependencies, which in a couple of cases resulted in me reducing the library size by 90%, but this is still Node.js where that isn't such a huge deal as it'd have been on the frontend.
Now there's an unresolved one, which is the Node.js streams vs WebStreams, and that is currently a HUGE mess. It's a complex topic on its own, but it's made a lot more complex by having two different streaming standards that are hard to match.
Web parity was "always" going to happen, but the refusal to add ESM support, and then when they finally did, the refusal to have a transition plan for making ESM the default, and CJS the fallback, has been absolutely grating for the last many years.
Also, "fetch" is lousy naming considering most API calls are POST.
const post = (url) => fetch(url, {method:"POST"})That's said there are npm packages that are ridiculously obsolete and overused.
`const { styleText } = require('node:util');`
Docs: https://nodejs.org/api/util.html#utilstyletextformat-text-op...
text: {
angry : "\u001b[1m\u001b[31m",
blue : "\u001b[34m",
bold : "\u001b[1m",
boldLine : "\u001b[1m\u001b[4m",
clear : "\u001b[24m\u001b[22m",
cyan : "\u001b[36m",
green : "\u001b[32m",
noColor : "\u001b[39m",
none : "\u001b[0m",
purple : "\u001b[35m",
red : "\u001b[31m",
underline: "\u001b[4m",
yellow : "\u001b[33m"
}
And then you can call that directly like: `${vars.text.green}whatever${vars.text.none}`;Using a library which handles that (an a thousand other quirks) makes much more sense
The real issue is invented here syndrome. People irrationally defer to libraries to cure their emotional fear of uncertainty. For really large problems, like complete terminal emulation, I understand that. However, when taken to an extreme, like the left pad debacle, it’s clear people are loading up on dependencies for irrational reasons.
Also, I'm guessing if I pipe your logs to a file you'll still write escapes into it? Why not just make life easier?
1. Node has built in test support now: looks like I can drop jest!
2. Node has built in watch support now: looks like I can drop nodemon!
(I haven't had much problem with TypeScript config in node:test projects, but partly because "type": "module" and using various versions of "erasableSyntaxOnly" and its strict-flag and linter predecessors, some of which were good ideas in ancient mocha testing, too.)
At the end it's just tests, the syntax might be more verbose but Llms write it anyway ;-)
They’re great at mocking to kingdom come for the sake of hitting 90% coverage. But past that it seems like they just test implementation enough to pass.
Like I’ve found that if the implementation is broken (even broken in hilariously obvious ways, like if (dontReturnPancake) return pancake; ), they’ll usually just write tests to pass the bad code instead of saying “hey I think you messed up on line 55…”
The problem isn't in the writing, but the reading!
I tried making a standalone executable with the command provided, but it produced a .blob which I believe still requires the Node runtime to run. I was able to make a true executable with postject per the Node docs[1], but a simple Hello World resulted in a 110 MB binary. This is probably a drawback worth mentioning.
Also, seeing those arbitrary timeout limits I can't help but think of the guy in Antarctica who had major headaches about hardcoded timeouts.[2]
[1]: https://nodejs.org/api/single-executable-applications.html
[1]: https://notes.billmill.org/programming/javascript/Making_a_s...
[2]: https://github.com/llimllib/node-esbuild-executable#making-a...
I hope you can appreciate how utterly insane this sounds to anyone outside of the JS world. Good on you for reducing the size, but my god…
Go binaries weight 20mb for example.
It says: "You can now bundle your Node.js application into a single executable file", but doesn't actually provide the command to create the binary. Something like:
npx postject hello NODE_SEA_BLOB sea-prep.blob \
--sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2Look up dialectical hedging. Dead AI giveaway.
It does tell you that if even 95% of HN can't tell, then 99% of the public can't tell. Which is pretty incredible.
The forest is darkening, and quickly.
Here, I'd hazard that 15% of front page posts in July couldn't pass a "avoids well-known LLM shibboleths" check.
Yesterday night, about 30% of my TikTok for you page was racist and/or homophobic videos generated by Veo 3.
Last year I thought it'd be beaten back by social convention. (i.e. if you could showed it was LLM output, it'd make people look stupid, so there was a disincentive to do this)
The latest round of releases was smart enough, and has diffused enough, that seemingly we have reached a moment where most people don't know the latest round of "tells" and it passes their Turing test., so there's not enough shame attached to prevent it from becoming a substantial portion of content.
I commented something similar re: slop last week, but made the mistake of including a side thing about Markdown-formatting. Got downvoted through the floor and a mod spanking, because people bumrushed to say that was mean, they're a new user so we should be nicer, also the Markdown syntax on HN is hard, also it seems like English is their second language.
And the second half of the article was composed of entirely 4 item lists.
[0] - https://www.youtube.com/watch?v=cIyiDDts0lo
[1] - https://blog.platformatic.dev/http-fundamentals-understandin...
I did some testing on an M3 Max Macbook Pro a couple of weeks ago. I compared the local server benchmark they have against a benchmark over the network. Undici appeared to perform best for local purposes, but Axios had better performance over the network.
I am not sure why that was exactly, but I have been using Undici with great success for the last year and a half regardless. It is certainly production ready, but often requires some thought about your use case if you're trying to squeeze out every drop of performance, as is usual.
In many ways, this debacle is reminiscent of the Python 2 to 3 cutover. I wish we had started with bidirectional import interop and dual module publications with graceful transitions instead of this cold turkey "new versions will only publish ESM" approach.
Hoisting/import order especially when trying to mock tests.
Whether or not to include extensions, and which extension to use, .js vs .ts.
Things like TS enums will not work.
Not being able to import TypeScript files without including the ts extension is definitely annoying. The rewriteRelativeImportExtensions tsconfig option added in TS 5.7 made it much more bearable though. When you enable that option not only does the TS compiler stop complaining when you specify the '.ts' extension in import statements (just like the allowImportingTsExtensions option has always allowed), but it also rewrites the paths if you compile the files, so that the build artifacts have the correct js extension: https://www.typescriptlang.org/docs/handbook/release-notes/t...
Sometimes I also read the proposals, https://github.com/tc39/proposals
I really want the pipeline operator to be included.
https://github.com/sindresorhus/execa/blob/main/docs/bash.md
We once reported an issue and it got fixed really quickly. But then we had troubles connecting via TLS (mysql on google cloud platform) and after a long time debugging found out the issue is actually not in deno, but in RustTLS, which is used by deno. Even a known issue in RustTLS - still hard to find out if you don't already know what you are searching for.
It was then quicker to switch to nodejs with a TS runner.
Hard to imagine that this wasn't due to competition in the space. With Deno and Bun trying to eat up some of the Node market in the past several years, seems like the Node dev got kicked into high gear.
new Error("something bad happened", {cause:innerException})
The demonstration code emits events, but nothing receives them. Hopefully some copy-paste error, and not more AI generated crap filling up the internet.
They've also been around for years as another poster mentioned.
The list of features is nice, I suppose, for those who aren't keeping up with new releases, but IMO, if you're working with node and js professionally, you should know about most, if not all of these features.
It's definitely awesome but doesn't seem newsworthy. The experimental stuff seems more along the lines of newsworthy.
Yes. It's been around and relatively stable in V8/Node.js for years now.
I highly recommend the `erasableSyntaxOnly` option in tsconfig because TS is most useful as a linter and smarter Intellisense that doesn't influence runtime code:
Also hadn't caught up with the the `node:` namespace.
1. new technologies
2. vanity layers for capabilities already present
It’s interesting to watch where people place their priorities given those two segments
Such as?
Why should it matter beyond correctness of the content, which you and the author need to evaluate either way.
Personally, I'm exhausted with this sentiment. There's no value in questioning how something gets written, only the output matters. Otherwise we'd be asking the same about pencils, typewriters, dictionaries and spellcheck in some pointless persuit of purity.
try {
// Parallel execution of independent operations
const [config, userData] = await Promise.all([
readFile('config.json', 'utf8'),
fetch('/api/user').then(r => r.json())
]);
...
} catch (error) {
// Structured error logging with context
...
}
This might seem fine at a glance, but a big grip I have with node/js async/promise helper functions is that you can't differ which promise returned/threw an exception.In this example, if you wanted to handle the `config.json` file not existing, you would need to somehow know what kind of error the `readFile` function can throw, and somehow manage to inspect it in the 'error' variable.
This gets even worse when trying to use something like `Promise.race` to handle promises as they are completed, like:
const result = Promise.race([op1, op2, op3]);
You need to somehow embed the information about what each promise represents inside the promise result, which usually is done through a wrapper that injects the promise value inside its own response... which is really ugly. // Parallel execution of independent operations
const [
{ value: config, reason: configError },
{ value: userData, reason: userDataError },
] = await Promise.allSettled([
readFile('config.json', 'utf8'),
fetch('/api/user').then(r => r.json())
]);
if (configError) {
// Error with config
}
if (userDataError) {
// Error with userData
}
When dealing with multiple parallel tasks that I care about their errors individually, I prefer to start the promises first and then await for their results after all of them are started, that way I can use try catch or be more explicit about resources: // Parallel execution of independent operations
const configPromise = readFile('config.json', 'utf8')
const userDataPromise = fetch('/api/user').then(r => r.json())
let config;
try {
config = await configPromise
} catch (err) {
// Error with config
}
let userData;
try {
userData = await userDataPromise
} catch (err) {
// Error with userData
}
Edit: added examples for dealing with errors with allSettled[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
If the config file not existing is a handleable case, then write a "loadConfig" function that returns undefined.
probably 70 to 80% of JS users have barely any idea of the difference because their tooling just makes it work.
But for larger and more complex projects, I tend to use Vitest these days. At 40MBs down, and most of the dependency weight falling to Vite (33MBs and something I likely already have installed directly), it's not too heavy of a dependency.
expect(bar).toEqual(
expect.objectContaining({
symbol: `BTC`,
interval: `hour`,
timestamp: expect.any(Number),
o: expect.any(Number),
h: expect.any(Number),
l: expect.any(Number),
c: expect.any(Number),
v: expect.any(Number)
})
);Also CommonJS does not support tree shaking.
Edit: the proof my point reside in the many libraries which have an open issue because, even if ESM, they don't support tree shaking
Instead of a .env.example (which quickly gets out of date), it uses a .env.schema - which contains extra metadata as decorator comments. It also introduces a new function call syntax, to securely load values from external sources.
Node test also I dont think is great, because in isomorphic apps you’ll have 2 syntax for testing.
I think the permissions are the core thing we should do, even if we run the apps in docker/dev containers.
Aliases is nice, node:fetch but I guess will break all isomorphic code.
A couple of things seem borrowed from Bun (unless I didn't know about them before?). This seems to be the silver lining from the constant churn in the Javascript ecosystem
> Top-Level Await: Simplifying Initialization
This feels absolutely horrible to me. There is no excuse for not having a proper entry-point function that gives full control to the developer to execute everything that is needed before anything else happens. Such as creating database connections, starting services and connecting to APIs, warming up caches and so on. All those things should be run (potentially concurrent).
Until this is possible, even with top-level await, I personally have to consider node.js to be broken.
> Modern Testing with Node.js Built-in Test Runner
Sorry, but please do one thing and do it well.
> Async/Await with Enhanced Error Handling
I wish had JVM-like logging and stack traces (including cause-nesting) in node.js...
> 6. Worker Threads: True Parallelism for CPU-Intensive Tasks
This is the biggest issue. There should be really an alternative that has builtin support for parallelism that doesn't force me to de/serialize things by hand.
---
Otherwise a lot of nice progress. But the above ones are bummers.
Maybe it needs a compile-time macro system so we have go full Java and have magical dependency injection annotations, Aspect-Oriented-Programming, and JavascriptBeans (you know you want it!).
Or maybe it needs to go the Ruby/Python/SmallTalk direction and add proper metaprogramming, so we can finally have Javascript on Rails, or maybe uh... Djsango?
Rather than taking the logical focus on making money, it is wasting time on shuffling around code and being an architecture astronaut with the main focus on details rather than shipping.
One of the biggest errors one can make is still using Node.js and Javascript on the server in 2025.
I often wonder about a what-if, alternate history scenario where Java had been rolled out to the browser in a more thoughtful way. Poor sandboxing, the Netscape plugin paradigm and perhaps Sun's licensing needs vs. Microsoft's practices ruined it.
I see it being used since over 25 years for the Austrian national broadcaster. Based at least originally on rhino, so it's also mixed with the Java you love. Fail to see the big issue as it's working just fine for such a long time.
> Perhaps the technology that you are using is loaded with hundreds of foot-guns
"Modern features" in Node.js means nothing given the entire ecosystem and its language is extremely easy to shoot yourself in the foot.
I have found this to not be true.
It's still single-threaded, it still uses millions of tiny files (making startup very slow), it still has wildly inconsistent basic management because it doesn't have "batteries included", etc...
But yes there are downsides. But the biggest ones you brought up are not true.
Try it for yourself:
> node
> Promise.all([Promise.reject()])
> Promise.reject()
> Promise.allSettled([Promise.reject()])
Promise.allSettled never results in an unhandledRejection, because it never rejects under any circumstance.
process.on("uncaughException", (e) => {
console.log("uncaughException", e);
});
try {
const r = await Promise.all([
Promise.reject(new Error('1')),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('2'), 1000));
}),
]);
console.log("r", r);
} catch (e) {
console.log("catch", e);
}
setTimeout(() => {
console.log("setTimeout");
}, 2000);
Produces: alvaro@DESKTOP ~/Projects/tests
$ node -v
v22.12.0
alvaro@DESKTOP ~/Projects/tests
$ node index.js
catch Error: 1
at file:///C:/Users/kaoD/Projects/tests/index.js:7:22
at ModuleJob.run (node:internal/modules/esm/module_job:271:25)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:547:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:116:5)
setTimeout
So, nope. The promises are just ignored.I definitely had a crash like that a long time ago, and you can find multiple articles describing that behavior. It was existing for quite a time, so I didn't think that is something they would fix so I didn't keep track of it.
Bun being VC-backed allows me to fig-leaf that emotional preference with a rational facade.
Not to say Deno doesn't try, some of their marketing feels very "how do you do fellow kids" like they're trying to play the JS hype game but don't know how to.
Because its Node.js compat isn't perfect, and so if you're running on Node in prod for whatever reason (e.g. because it's an Electron app), you might want to use the same thing in dev to avoid "why doesn't it work??" head scratches.
Because Bun doesn't have as good IDE integration as Node does.
- isolated, pnpm-style symlink installs for node_modules
- catalogs
- yarn.lock support (later today)
- bun audit
- bun update —interactive
- bun why <pkg> helps find why a package is installed
- bun info <pkg>
- bun pm pkg get
- bun pm version (for bumping)
We will also support pnpm lockfile migration next week. To do that, we’re writing a YAML parser. This will also unlock importing YAML files in JavaScript at runtime.
(closing the circle)
online writing before 2022 is the low-background steel of the information age. now these models will all be training on their own output. what will the consequences be of this?
While I can see some arguments for "we need good tools like Node so that we can more easily write actual applications that solve actual business problems", this seems to me to be the opposite.
All I should ever have to do to import a bunch of functions from a file is
"import * from './path'"
anything more than that is a solution in search of a problem
Feels unrelated to the article though.