Show HN: Nub – A Bun-like all-in-one toolkit for Node.js

github.com

270 points by colinmcd 2 days ago

Colin here, creator of Nub. I’ve had the general shape of this in mind for years. Nub runs your code with stock `node`, augmented with a `--require` preload hook[0] that adds a transpiler (oxc-powered, packaged as a Node-API add-on), registers a module resolution hook[1], and injects polyfills as needed for APIs like `Worker`, `Temporal`, etc. All purely additive, your code ultimately runs using Node’s actual engine & stdlib implementations.

[0] https://nodejs.org/api/cli.html#-require-module

[1] https://nodejs.org/api/module.html#moduleregisterhooksoption...

pier25 1 day ago

Very cool idea which makes a lot of sense. Bun provides more (eg db drivers) but the DX is certainly a big part of its appeal.

For reference, the main author of Nub is Colin McDonnell who created Zod and even worked at Bun at some point.

  • colinmcd 1 day ago

    Indeed, Nub intentionally introduces no Nub-specific APIs: no Nub global, no nub: prefixed built-in modules, no Nub-named config file / lockfile, no "nub" field in package.json, not even any NUB_ environment variables. Most of the stuff Bun added is better as a proper dependency imo.

eyelidlessness 2 days ago

I’m surprised to see this using a `--require` hook (rather than `--import`). Maybe something’s changed significantly since I was looking into building some similar functionality… but it makes me wonder about nuances in nub’s ESM support.

(When I was investigating this it was very early in Node’s `--import` story, but there were several edge cases with the more common ESM-to-CJS approaches that I wanted to address. Most were probably exceedingly niche concerns, but I’d expect top-level await to affect a meaningful subset of users.)

  • colinmcd 2 days ago

    We use this to register our preload purely for performance reasons. In this and many other cases CommonJS is still faster than ESM. Using --require is about 0.5ms overhead vs 4.6ms for --import (on my M1 Macbook Pro).

    Relatedly Node.js recently (2025) introduced a synchronous version of its resolver hook registration API (`module.registerHooks()`) specifically to improve performance over the old async `module.register()` API. It was a big unblocker for Nub. For the interested, the async API added 19ms fixed registration overhead + about 130us additional overhead per import.

    Which flag Nub uses here doesn't impact userland at all, TLA is supported wherever it's supported by Node.js itself.

    • eyelidlessness 1 day ago

      Thanks! From what you say here and what I see in the docs, it looks like everything is much simpler and more robust than when I was exploring the space. I’m happy to see that, and thrilled it’s mature enough now to support use cases like nub.

BrunoBernardino 11 hours ago

This is _very_ interesting, thanks for building and sharing it, Colin!

I've got a couple of decent-sized products running on Deno (and I've been a fan since it came out, not so much since 1.0 and 2.0, but still feel it's better than Node), but Node has been catching up and Deno has slowed down, so this might be the "compatibility middleware" that'll get me to move things back to Node.

Will keep a close eye on it.

ssalbdivad 2 days ago

Just merged a PR migrating our entire monorepo to nub.

0 issues, ridiculously fast.

  • daavin 2 days ago

    You merged a PR migrating a shared monorepo using this within an hour of it being posted?

    • colinmcd 2 days ago

      It entered public beta last week, but just getting on HN now.

      • phpnode 1 day ago

        Also, ssalbdivad is your cofounder, just in case you’d forgotten!

vmsp 1 day ago

Hasn't Node been able to run TypeScript for a couple of versions? Why's the transpiler needed?

  • conartist6 1 day ago

    It says "injects polyfills as needed for APIs like `Worker`, `Temporal`, etc"

  • zamadatix 1 day ago

    Node's built-in Typescript support is just for type stripping. That works for a massive amount of TypeScript syntax, but not all of it. It also doesn't bother actually checking the types, just removing them so it runs. You also lose things like tsconfig.

    This looks to support both the built-in stripping as well as actual TypeScript.

    • colinmcd 1 day ago

      Exactly this. TypeScript also ended up being many users' entrypoint to JSX, decorators, etc as it became a catchall transpiler surface so respecting tsconfig is vital here (Node understandably has a policy against this). Plus there are extensionless imports, the "phantom" .js imports (pointing to .ts files), etc. List goes on. Type stripping ends up working out of the box for virtually no real world projects sadly.

      https://nubjs.com/docs/runtime/typescript

keks0r 12 hours ago

Does this mean, that the full node compatibility purely depends on oxc for transpiling? So oxc compatibility = nub compatibility?

awaseem 2 days ago

I saw this on twitter and loved it, such a good move on your part Colin. Hope the project picks up tons of steam!

haburka 1 day ago

I actually really like this! Great choices all around.

ivanjermakov 2 days ago

Respect for embracing existing tech instead of rewriting a worse version of it. Wonder where we would be today if all alternative-building effort went to Node instead (with proper leadership).

  • hungryhobbit 2 days ago

    Fundamentally you can't fix a lot of things with this approach.

    Simple example: Node is the only serious OSS software I know of that has no way to document its config (in the config file itself). It's moronic! The Node people just adopted JSON without a thought, and then refused to consider any alternatives (even "JSON with comments").

    When an organization digs into bad decisions, the only way to fix them is to start something new. The entire JS ecosystem will never have documentation on its config as long as everyone keeps building on top of Node.

    (And there are many other issues like this in the Node ecosystem; the utter absurdity of not being able to document config is just my personal pet peeve.)

    • johnfn 2 days ago

      > Simple example: Node is the only serious OSS software I know of that has no way to document its config (in the config file itself). It's moronic! The Node people just adopted JSON without a thought, and then refused to consider any alternatives (even "JSON with comments").

      Tangential but this also drives me absolutely nuts. If I have to see `"//": "some comment"` one more time I'm gonna lose it.

    • colinmcd 1 day ago

      EDIT: Sorry, I understand you're talking about package.json. Would be fun to try to get the Node & package mgmt teams aligned to add support for comments in the package.json. Bun tried and failed to do this (requires ecosystem coordination).

      Nub could absolutely support a config file and use it to set NODE_OPTIONS or flags in the node child process. There's no reason to throw out the baby with the bathwater due to DX concerns like this. That's a key part of the concept Nub is trying to prove. (To be clear I'm quite content to conform to Node's no-config-file policy at the moment.)

    • matt_kantor 1 day ago

      > Simple example: [package.json is JSON]

      Why can't you fix this while embracing existing tech? I can imagine monkeypatching Node & NPM to add support for JSONC or JSON5 or whatever in the same way that Nub adds various features via monkeypatching. Is there some architectural reason that can't work?

      You'd need `npm publish` to compile it down to plain JSON when publishing, but that seems like an okay compromise.

      • conartist6 1 day ago

        Yeah stripping out comments at publish time would have been the smartest move. Keep in mind though that a git repo can be used as a package, so there isn't necessarily going to be an explicit publish step

    • afavour 1 day ago

      Isn't this a problem contained to npm more than Node itself?

      I'm imagining pnpm or others being able to adopt a package.json file that allows comments, then, when actually publishing, ensuring the published package.json is regular JSON.

      • thayne 1 day ago

        No. Node.js uses package.json as well

    • jdxcode 1 day ago

      I agree that it sucks not being able to have comments in package.json, but I think it's the right call to not adopt something like jsonc. It would break so much tooling at this point I don't think it would be worth it.

  • johnfn 2 days ago

    You might remember the io.js fork of Node.js back in 2014. Node was stagnating, a bunch of people forked it into io.js, which eventually got merged back into Node and got it back on track. Or, going further back, CoffeeScript, a "fork" of JS that had its best ideas adopted back into ES5.

    A small scrappy team can prove out a good idea because failure is not a catastrophic risk to them. In short, forks are part of a healthy ecosystem.

    • hootz 2 days ago

      It is still happening, a lot of things are still being adopted by Node after being available on other runtimes. They aren't forks, but they still provide pressure towards progress.

    • psygn89 1 day ago

      Beautifully stated reminder :)

    • llbbdd 1 day ago

      I had a lot of very good times with CoffeeScript but I'll never forgive it for having implicit returns. So many subtle event-bubbling bugs caused by that.

austin-cheney 1 day ago

The readme says Websockets support is native from Node 22, except Node does not have a native Websockets library. Their link for Websockets standard goes to MDN, which is fine except that only describes the WHATWG user interface and nothing about the protocol or how Websockets works.

It feels like this is missing something or using a nonnative library as a supplemental addition.

  • soulofmischief 1 day ago

    Node supports a built-in WebSocket client since 22, but not a server.

    • austin-cheney 1 day ago

      In that case I guess this would be much more a Node complaint. I did find this page:

      https://nodejs.org/learn/getting-started/websocket

      It is under the site's learn tab, but never mentioned in the API/library documentation.

      It's also weird because the only difference between a WebSocket client and server is that a server is a connection listener that responds to the handshake. The client is responsible for absolutely everything else: send messages, receive messages, initiate the connection, kill the connection, ping/pong. After the client accepts the server's handshake response the server becomes the other client performing exactly the same functionality. Node could have been doing that all along since adding client support.

gorjusborg 2 days ago

Very smart. You can't lose all your customers for vibe-coding a migration to Rust if you are already written in Rust ;)

  • Zambyte 2 days ago

    They'll get bought out by OpenAI and convert the project to Zig

    • airstrike 2 days ago

      huh, is OpenAI embracing Zig specifically? TIL

      • allthetime 2 days ago

        It’s a joke about how Anthropic bought bun and then rewrote bun from zig to rust with a giant one week vibe code. The joke hinges on the fact that this would be the opposite (OpenAI, nub, rust to zig)

        • airstrike 2 days ago

          oh LOL I'm slow today ig

  • lacoolj 2 days ago

    It also helps if you're already vibe-coded and don't have customers to begin with

  • maxloh 1 day ago

    This project is already highly vibe-coded. Claude is the second contributor of the repo.

  • coneonthefloor 1 day ago

    Yea, not sure what “written in rust” adds to this. I would have thought that you could get the same functionality with a few shell scripts and a package.json.

    • afavour 1 day ago

      I assume speed.

      But also, I know “rewrite in Rust” is a meme but if you’re doing something new, why not? I can’t see a good argument that a pile of shell scripts would be a superior way to organise things.

  • pier25 1 day ago

    There's a difference between running your app on a vibe coded runtime or just local DX tools though.

  • leobuskin 1 day ago

    “If you are already vibe-coded in Rust”, probably is better, Nub has a strong smell all the way (which is ok, just funny to compare with Bun).

    P.S. The entire Bun’s anti-AI hysteria makes me very sad, sometimes it feels like a directed campaign

sgarrity 2 days ago

I didn't even click on the link. I just came to give the author a hat-tip on the project name. Well played.

  • colinmcd 2 days ago

    Thanks :) Highly recommend clicking the link too!

kandros 2 days ago

Love the idea, learning a lot of interesting things about node hooks by reading docs and some code

jpambrun 1 day ago

Am I expected to be able to run this in production on the backend, or do I still need to transpile and bundle? Do we expect the performance and memory overhead to be negligible. What would be the expectations on terms of added attack surface?

  • colinmcd 1 day ago

    Right now, you should use Nub on the backend if you are relying on its augmentations. If you specifically want to disable Nub's augmentations (so you have a guarantee that your app/script will "just work" with regular Node, there's a couple ways to disable it.

      NODE_COMPAT=0 nub index.ts
      nub --node index.ts
    
    

    I'll investigate a `nub build` that would do the transpilation upfront and properly chunk/bundle a prod build. It's a good idea. But yes, Nub's overhead (both time and space) is generally negligible relative to Node itself.

    Re: added attack surface: the most obvious one is that Nub loads .env files (same as Bun/Next/Vite) so be aware of that. All of Node's permission flags are passed through as well. I won't claim there's no additional attack surface, but it doesn't have much surface area, just a Rust wrapper that spawns `node` ultimately.

    • BearOso 1 day ago

      I'm seeing lots of bots/agents misusing backticks like this. It's because people are writing agent plans in markdown and the agent thinks it's appropriate everywhere. Maybe you shouldn't be running all your comment replies through Claude.

      • fartcoin67 1 day ago

        Dude people have been doing this since long before LLMs. It's more an artifact of Slack than anything.

        • BearOso 3 hours ago

          Slack uses this? Thanks for the info. I've never been forced into Slack, so I wasn't aware of that. Either way, it's stupid to use in places it doesn't induce formatting.

      • jitl 1 day ago

        what are you talking about

        • BearOso 2 hours ago

          Backticks ` ` are used to trigger code-style formatting in markdown. Since all the coding AIs are trained almost entirely on github, you see it everywhere in their output. They'll use it even in places where it's supposed to be just plain text, and almost always in git log messages, which by spec have no formatting.

          • jitl 2 hours ago

            i write markdown-ish text in any text box including using backticks for command names like `cat`, or use * for *emphasis* just like i did on IRC in the 2000s. these conventions in human plain text conversation predate markdown and AI by decades. that was the point of markdown: formalizing those kinds of conventions enough to turn them into deterministic rich styling.

            HN itself renders * as emphasis, but not ` for "code"; this doesn't mean HN is a broken markdown machine or that i'm a bot. many hn comments use plain text conventions for links, see examples that use footnote style markdown-like links[0], I was [doing markdown links here back in 2017](https://news.ycombinator.com/item?id=14763397) even though they've never been parsed by HN.

            This kind of semantic formalism in text goes way back, its not so different from something like Chicago style citations first published in 1906 [1].

            [0]: https://news.ycombinator.com/item?id=47258996

            [1]: https://www.chicagomanualofstyle.org/dam/jcr:bba47b07-61ba-4...

skybrian 2 days ago

> TypeScript-friendly resolution: extensionless imports, tsconfig.json#paths

I’m wondering how that works. Deno has very complicated import resolution, so building my own import resolver to be compatible with it is a bit of a pain. (This is for a custom lint-like tool.)

bookernath 2 days ago

Nice, I think this fills a niche. Does it work on cloudflare workers?

  • colinmcd 2 days ago

    Cloudflare Workers is a different runtime and has its own toolchain around it. Nub could theoretically support it when executing files (spawn `wrangler dev` instead of `node` if wrangler.toml is detected or something) but really I'm focused on making the Node.js experience as good as possible.

    The other pieces of the toolkit could absolutely be used: package manager, script runner, package runner. Works with anything that implements the Node module resolution algorithm (actually Yarn PnP also works out of the box...).

zarzavat 1 day ago

This looks fantastic.

One suggestion, for package management you should just wrap pnpm, my desire to move to anything else is nil.

  • colinmcd 1 day ago

    You can continue using pnpm if you want. Nub is a la carte. Nub's runtime or script runner functionality will work with any package manager that uses node_modules (actually Yarn PnP is also supported out of the box).

    You can also move over to Nub from pnpm incrementally if you want the performance or security wins. It has complete pnpm compatibility: flag for flag identical CLI, reads and writes from your pnpm-lock.yaml, and respects your expecting configs completely. Nub and pnpm can be used simultaneously by different teammates without lockfile churn. This is a key design goal, and its what I mean by "incrementally adoptable". But yeah `nub install` will almost certainly work in your repo out of the box. (File an issue otherwise.)

    https://nubjs.com/docs/install/pnpm

    Same is true for npm, yarn, and bun as well. It detects your current package manager (based on package.json#packageManager or lockfile existence) and runs in "compatibility mode" for that specific package manager.

    https://nubjs.com/docs/install#config-it-reads

campak 1 day ago

big fan of nub and nubnub the mascot. Seriously though, this is a great project. I'm super intruiged and been using for the past week or at least since it released

cozzyd 1 day ago

I know some people who pronounce n00b like nub, which makes this sort of funny to me. (Maybe it's even intentional?)

montroser 2 days ago

Nice. Can we get `nub --compile` up in there like Bun has?

  • colinmcd 2 days ago

    Coming very soon!

    • allthetime 2 days ago

      I will seriously consider migrating once this exists! I couldn’t imagine deploying any other way now. I can never go back from SCPing a single binary to my server and just hitting reset on the service.

vivzkestrel 1 day ago

- eli 5 : what is so great about this

nsonha 1 day ago

what's wrong with bun? And how is this better?

  • jitl 1 day ago

    for me it’s the segfaults. never had node segfault on me.

GL26 2 days ago

nice ! does this work on docker containers ?

  • colinmcd 2 days ago

    Yep, full support on macOS, Linux, Windows. No official image yet (I'll start on this now) but you can get started with something like this.

      FROM node:26-slim
      RUN npm i -g @nubjs/nub
    

    Works with any Node version down to 18.19 but recommend 22.15+ for best performance (that's when synchronous registerHooks was introduced[0])

    [0] https://nodejs.org/api/module.html#moduleregisterhooksoption...