DEV Community

Cover image for Monorepo with Bun
Vincenzo
Vincenzo

Posted on

Monorepo with Bun

Hello

Had this idea recently. I built this project management tool kinda like Jira, called kiffari, and now I’m working on turning it into something that runs locally, using markdown files as a database. It’s called kiffarino. The whole point is to keep tickets right next to the code that handles them

But this post isn’t really about that.

It’s about how much I’ve gotten used to bun as my go-to JavaScript runtime. I almost forgot it can also transpile TypeScript for Node, work as a package manager, and handle monorepos too. It does all of it.

Over the past few months I’ve moved most of my stuff to bun. It just makes sense. I love JavaScript. It’s the language I use the most and the one I feel fastest with. TypeScript grew on me too. Not because of the safety everyone talks about, but because the type hints are nice. It’s basically inline docs that actually work

What used to really piss me off with TypeScript was the setup. Every time I started a new side project it was the same annoying routine

  1. Init the project with npm or pnpm
  2. Copy a massive tsconfig from some other repo
  3. Try to run it with ts-node or tsx or figure out how to build it properly

It’s not awful if you have a good starter or template, but still, too much time wasted before you even write a line of code.

If also you want to have monorepo, with shared libs, the headache gets increasingly bigger, as you need to multiply the effort of all of those boilerplate nonsense.

I have used pnpm and nx for work, it works really well but the boilerplate you need to just get going is just too much.

Bundle that, build this, transpile that other one and by the time you get going that initial idea is getting boring and your side project is dead.

I have used bun so far as a runtime, monorepo manager and a package manager, so I decided to give it a go as a transpiler too.

Long story short, it worked.

In kiffarino, I have the following setup:

  • a hono backend
  • a svelte5 spa
  • a shared lib to share types and functions between the 2 repos. (Endpoint results for example)

I had done that before in other repos (alicarti a game framework for websocket games), but using bun as a runtime, so it was all good easy and smooth.

Here I wanted to create a single executable, to be compatible with node and be able to publish it on npm as a package I can use everywhere.

And it all just works.

How did I do that?

Easy:

Backend

"build": "bun build ./src/index.ts --outdir ./dist/ --target node"

This just transpiles and makes it runnable by node with only one command.

Frontend

"build": "bunx --bun vite build"

vite handles it really well but run via bunx

Shared libs

{
  "name": "@kiffarino/shared",
  "module": "index.ts",
  "type": "module",
  "devDependencies": {
    "@types/bun": "latest"
  },
  "peerDependencies": {
    "typescript": "^5"
  }
}
Enter fullscreen mode Exit fullscreen mode

Only adding that index.ts and it looking like this

export * from "./config";
export * from "./models";
Enter fullscreen mode Exit fullscreen mode

it allows me to use those libs everywhere in the monorepo.

It just works wonders and to setup the workspaces, as suggested from their docs, you only need to do two things:

This in the root packages.json

  "workspaces": [
    "be",
    "web",
    "shared"
  ],
Enter fullscreen mode Exit fullscreen mode

and as long as those packages have a name in them, this what you can put so the subrepo can see stuff exported by other ones.

  "dependencies": {
    "@kiffarino/shared": "workspace:*",
Enter fullscreen mode Exit fullscreen mode

and to use them you can just

import { Ticket } from "@kiffarino/shared"; // <-- isnt this great?
import type { Context } from "hono";

[...]

export async function create(c: Context) {
  const body = (await c.req.json()) as { title: string };
  [...]
  const ticket = Ticket.create(body.title);
  return c.json(
    {
      result: ticket,
    },
    201
  );
}
Enter fullscreen mode Exit fullscreen mode

Setup finished.

How to run scripts concurrently you ask?

"dev": "bun --filter '*' dev",
"check": "bun --filter '*' check",
Enter fullscreen mode Exit fullscreen mode

this will look in * all of the workspaces and check if a dev script is in the package.json and run it.
The second one is to do a typecheck instead.

In the case of a hono repo is a bit more complex as bun does not do typechecking.

But adding this is enough:

"check": "bunx tsc --noEmit -p tsconfig.json",
Enter fullscreen mode Exit fullscreen mode

In short, bun has completely changed how I handle JavaScript and TypeScript projects. It cuts through the usual setup headaches, speeds up development, and keeps everything simple—even in monorepos. If you’re tired of wrestling with configs and build pipelines, give bun a shot. It just works.

Top comments (2)

Collapse
 
it-wibrc profile image
Waffeu Rayn • Edited

Thank you for the article. It helps me resolve the build problem for all the repo using one command at the root of the monorepo

But the link bun.sh/guides/install/workspaces t the doc shows us the way to add package to the workspaces and it's not the same as in the article

Collapse
 
vikkio88 profile image
Vincenzo

pleasure man, I do those just to keep track of those little gotchas lol