Bun.js is a new javascript runtime which is released as an alternative to Node and Deno. Bun is different though. It is built with performance and ease of use in mind. It is a batteries included tool for javascript development.
It has more or less the same APIs as Node.js, which is a big relief for developers as we need not learn yet another set of APIs.
One of the other awesome features of Bun is, it has complete support for existing npm packages. Therefore, from the start, Bun has an excellent and active collection of third party modules for almost everything.
And the best part, typescript, JSX, TSX, etc. just work without anything config!
From their site: “Run, test, transpile, and bundle JavaScript & TypeScript projects — all in Bun. Bun is a new JavaScript runtime built for speed, with a native bundler, transpiler, test runner, and npm-compatible package manager baked-in.”
Sounds promising. In this article, let me summarize how to setup a simple full stack notes application using Boa.js(similar to Express) and a React frontend all using Bun!
Installing Bun.js
Installing Bun is simple. It is just a simple curl command. (Bun is not supported in Windows as of now).
curl -fsSL https://bun.sh/install | bash
You can also use npm if you prefer:
npm i -g bun
Hopefully, this is the last npm command you write 😝
Setting up a backend server
For our backend server, we will be using bao.js
To install Bao.js in Bun, it is just a single command:
bun create elysia ./Server
Here is a simple server setup using Bun and Elysia:
import { Elysia } from 'elysia'
const app = new Elysia()
interface INote {
id: string
content: string
}
const notes: INote[] = []
app.get('/ping', () => 'pong')
app.listen(3000)
console.log(`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`)
Now head onto port 3000 and you should see a “pong” text!
CRUD operations:
app.get('/:id', (ctx) => notes.filter(n => n.id === ctx.params.id))
app.post('/', (ctx) => {
const note = ctx.body as INote;
note.id = crypto.randomUUID()
notes.push(note);
return note;
})
app.put('/:id', (ctx) => {
const note = ctx.body as INote;
const currentNote = notes.find(n => n.id === ctx.params.id)
if (!currentNote) {
throw new Error("NOT_FOUND")
}
currentNote.content = note.content;
return currentNote;
})
app.delete('/:id', (ctx) => {
const currentNoteIdx = notes.findIndex(n => n.id === ctx.params.id)
if (currentNoteIdx < 0) {
throw new Error("NOT_FOUND")
}
notes.splice(1, currentNoteIdx);
return;
})
Simple error handler:
app.onError(({ code, error, set }) => {
if (error.message === 'NOT_FOUND') {
set.status = 404
return 'Not Found :('
}
})
Use the following command to start the server:
bun src/index.ts
Creating the frontend:
Creating the front-end using bun cli is similar:
bun create react ./Client
Bun by default supports front end transpiling of JSX, TSX and other files. We can create a simple component and use bun to serve the dev server:
import React from 'react'
import useSWR from 'swr'
import { API_PATH } from '../constants'
import { INote } from '../types'
export const ListNotes = () => {
const { data, error, isLoading }: { data: INote[]; error: any; isLoading: Boolean } = useSWR(
`${API_PATH}/`,
(url) => fetch(url).then((res) => res.json())
)
return (
<>
{isLoading && <p>Loading...</p>}
{error && <p>error occurred</p>}
{!isLoading && !error && data.length > 0 && (
<ul>
{data.map((note) => (
<li>{note.content}</li>
))}
</ul>
)}
{!isLoading && !error && data.length === 0 && <p>No data present</p>}
</>
)
}
Use bun dev to serve the files:
bun dev
Conclusion:
In this short article, we explored bun to get an idea of a simple full stack app. As far as tooling goes, bun is awesome. It has unparalleled performance compared to other bundlers and servers in the node ecosystem.
Here is the code: