app logoSearch

Explorer

  • Home Page
Ctrl+Btoggle sidebar
  1. Home
  2. 02 reference notes
  3. topics
  4. bun

Bun

Pendahuluan

apa itu bun?

Bun adalah runtime JavaScript yang sangat cepat, dirancang sebagai pengganti langsung (drop-in replacement) untuk Node.js.

Fitur Utama Bun

  • Bun Runtime: Lingkungan eksekusi JavaScript/TypeScript yang cepat dengan overhead mendekati nol, kompatibel dengan API Web standar (fetch, WebSocket, ReadableStream) dan modul Node.js (CommonJS & ESM). ^jduOOBC9
  • Bun Package Manager bun install: package manager yang diklaim hingga 30 kali lebih cepat daripada npm.
  • Bun Bundler bun build: Pembundel (bundler) yang dapat memaketkan file TypeScript, JSX, React, dan CSS untuk browser dan server.
  • Bun Test Runner bun test: Pelari uji (test runner) yang kompatibel dengan Jest dan mengutamakan TypeScript.
  • Dukungan TypeScript & JSX: Bun dapat menjalankan file .jsx, .ts, dan .tsx secara langsung tanpa perlu transpilasi tambahan.

perbedaan antara Bun dan Node.js

Fitur/AspekBunNode.js
Mesin JavaScriptJavaScriptCore (dari WebKit/Safari)V8 (dari Google Chrome)
Bahasa ImplementasiZigC++
Pendekatan ToolingAll-in-one (runtime, package manager, bundler, test runner built-in)Runtime saja (membutuhkan alat eksternal seperti npm/yarn/pnpm, Webpack/Rollup, Jest/Mocha)
Kecepatan StartupUmumnya lebih cepat (diklaim 4x lebih cepat dari Node.js)Relatif lebih lambat
Kecepatan EksekusiSeringkali menunjukkan performa lebih cepat, terutama pada tugas yang intensif CPU.Relatif lebih lambat dibandingkan Bun, tetapi sangat matang dan stabil.
Maturitas & EkosistemLebih baru, ekosistem masih berkembang.Sangat matang, ekosistem luas dan stabil digunakan di jutaan aplikasi produksi.

Install bun

$ curl -fsSL https://bun.com/install | bash

Menggunakan bun

Create Project

$ mkdir quickstart
$ cd quickstart
$ bun init
$ bun run index.ts

Setup Web Server

const server = Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response("Hello, Bun!");
  },
});
 
console.log(`Server running at http://localhost:${server.port}`);
$ bun run index.ts

Install Package

$ bun add figlet
import figlet from "figlet";
 
const server = Bun.serve({
  port: 3000,
  fetch(request) {
    const body = figlet.textSync("Hello, Bun!");
    return new Response(body);
  },
});
 
console.log(`Server running at http://localhost:${server.port}`);

Development Server

Dengan menggunakan bun --watch, kita bisa mengatur server untuk memantau perubahan pada file dan secara otomatis me-reload server saat ada perubahan.

{
  "scripts": {
    "start": "bun run index.ts",
    "dev": "bun --watch index.ts"
  }
}
$ bun run dev

Routing

import figlet from "figlet";
 
const server = Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
 
    if (url.pathname === "/") {
      const body = figlet.textSync("Hello, Bun & figlet!");
      return new Response(body);
    }
 
    if (url.pathname === "/about") {
      const body = figlet.textSync("About me!");
      return new Response(body);
    }
 
    if (url.pathname === "/contact") {
      const body = figlet.textSync("Contact us");
      return new Response(body);
    }
 
    return new Response("404!");
  },
});
 
console.log(`Server running at http://localhost:${server.port}`);

silahkan akses:

  • http://localhost:3000/
  • http://localhost:3000/about
  • http://localhost:3000/contact

Throw Error

const server = Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
 
    if (url.pathname === "/") {
      const body = figlet.textSync("Hello, Bun & figlet!");
      return new Response(body);
    }
    if (url.pathname === "/feed") {
      throw new Error("This page is not available yet!");
    }
 
    return new Response("404!");
  },
});
console.log(`Server running at http://localhost:${server.port}`);
20250715233544.png

Custom Error Response

const server = Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
 
    if (url.pathname === "/") {
      const body = figlet.textSync("Hello, Bun & figlet!");
      return new Response(body);
    }
 
    if (url.pathname === "/feed") {
      throw new Error("This page is not available yet!");
    }
 
    return new Response("404!");
  },
  error(error) {
    return new Response(`<pre>${error.message}</pre>`, {
      headers: {
        "Content-Type": "text/html",
      },
    });
  },
});
 
console.log(`Server running at http://localhost:${server.port}`);
  • Dengan menambahkan fungsi error, kita bisa mengatur bagaimana server menangani error yang terjadi. Dalam contoh ini, kita mengembalikan pesan error dalam format HTML.

Read File

const server = Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
 
    if (url.pathname === "/greet") {
      return new Response(Bun.file("./greet.txt"));
    }
 
    return new Response("404!");
  },
});
 
console.log(`Server running at http://localhost:${server.port}`);
  • Dengan menggunakan Bun.file, kita bisa membaca file secara langsung dan mengembalikannya sebagai respons. Dalam contoh ini, kita membaca file greet.txt dan mengembalikannya saat ada permintaan ke /greet.

Framework Rest API (elysia)

Elysia adalah framework untuk membuat REST API (seperti express) dengan Bun yang sangat cepat dan efisien. Elysia menyediakan berbagai fitur seperti routing, middleware, dan dukungan untuk berbagai format data.

$ bun add elysia bun-rest-api
$ cd bun-rest-api
$ bun run dev

Make Route

import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .get("/post/:id", ({ params: { id } }) => id)
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Dengan menggunakan Elysia, kita bisa membuat route dengan mudah. Dalam contoh ini, kita membuat route untuk root (/) dan route dinamis untuk mendapatkan post berdasarkan ID (/post/:id).
  • params adalah objek yang berisi parameter dari URL. Dalam contoh ini, kita mengambil ID dari URL dan mengembalikannya sebagai respons.

Post Request

import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .get("/post/:id", ({ params: { id } }) => {
    return { id: id, title: "Ini adalah Judul" };
  })
  .post("/post", (body) => {
    return {
      message: "Post created successfully",
      data: body,
    };
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Kita juga bisa membuat route untuk menerima permintaan POST. Dalam contoh ini, kita membuat route /post yang menerima data dari body permintaan dan mengembalikannya sebagai respons.

Get all routes

Dengan menggunakan wildcard *, kita bisa menangkap semua route yang tidak ditangani sebelumnya.

import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .get("/post/:id", ({ params: { id } }) => {
    return { id: id, title: "Ini adalah Judul" };
  })
  .post("/post", (body) => {
    return {
      message: "Post created successfully",
      data: body,
    };
  })
  .get("/track/*", () => {
    return "Get all routes";
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);

Context

import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .get("/post/:id", ({ params: { id } }) => {
    return { id: id, title: "Ini adalah Judul" };
  })
  .post("/post", (context) => {
    return {
      message: "Post created successfully",
      data: context,
    };
  })
  .get("/track/*", (context) => {
    return context;
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
{
  "message": "Post created successfully",
  "data": {
    "request": {},
    "store": {},
    "set": {
      "headers": {},
      "status": 200
    }
  }
}
  • Context adalah objek yang berisi informasi tentang permintaan dan respons. Dalam contoh ini, kita mengembalikan context pada route /post dan /track/*.
  • Context ini bisa digunakan untuk mengakses berbagai informasi seperti headers, query parameters, dan lainnya.
  • Return context pada route /track/* akan mengembalikan informasi lengkap tentang permintaan yang diterima.
    • Ini termasuk informasi tentang request, store, dan set yang berisi headers dan status.
    • Kita bisa menggunakan informasi ini untuk debugging atau logging.
    • Dengan menggunakan context, kita tidak dapat mengakses data dari body permintaan secara langsung. Untuk itu, kita perlu menggunakan middleware atau cara lain untuk mengakses body.
set function
import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .get("/post/:id", ({ params: { id } }) => {
    return { id: id, title: "Ini adalah Judul" };
  })
  .post("/post", ({ body, set }) => {
    set.status = 201; // Mengatur status code menjadi 201
    return body;
  })
  .get("/track/*", (context) => {
    return context;
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Dengan menggunakan fungsi set, kita bisa mengatur status code dan headers dari respons. Dalam contoh ini, kita mengatur status code menjadi 403 pada route /post.

Return JSON Response

import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/tracks", () => {
    return new Response(
      JSON.stringify({
        tracks: [
          { id: 1, title: "Track 1" },
          { id: 2, title: "Track 2" },
          { id: 3, title: "Track 3" },
        ],
      }),
      {
        headers: {
          "Content-Type": "application/json",
        },
      },
    );
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Kita bisa mengembalikan respons dalam format JSON dengan menggunakan JSON.stringify. Dalam contoh ini, kita mengembalikan daftar track dalam format JSON pada route /tracks.
Warning

headers harus diatur secara manual untuk mengembalikan respons dalam format JSON. Jika tidak, respons akan dianggap sebagai teks biasa.

Elysia juga menyediakan cara yang lebih sederhana untuk mengembalikan respons dalam format JSON:

import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/tracks", () => {
    return {
      tracks: [
        { id: 1, title: "Track 1" },
        { id: 2, title: "Track 2" },
        { id: 3, title: "Track 3" },
      ],
    };
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);

State dan Decorate

Dengan Elysia, kita bisa menyimpan data dalam state dan membuat fungsi dekorasi yang bisa digunakan di seluruh aplikasi.

import { Elysia } from "elysia";
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .state("version", "1.0.0")
  .decorate("getDate", () => Date.now())
  .get("/tracks", ({ store, getDate }) => {
    console.log("Store version:", store.version);
    console.log("Current date:", getDate());
    return {
      tracks: [
        { id: 1, title: "Track 1" },
        { id: 2, title: "Track 2" },
        { id: 3, title: "Track 3" },
      ],
      version: store.version,
      date: getDate(),
    };
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Dengan menggunakan state, kita bisa menyimpan data yang bisa diakses di seluruh aplikasi. Dalam contoh ini, kita menyimpan versi aplikasi dalam state.
  • Dengan menggunakan decorate, kita bisa membuat fungsi yang bisa digunakan di seluruh aplikasi. Dalam contoh ini, kita membuat fungsi getDate yang mengembalikan tanggal saat ini.
  • Kita bisa mengakses state dan fungsi dekorasi ini di dalam route handler. Dalam contoh ini, kita mengakses versi aplikasi dan tanggal saat ini pada route /tracks.
  • Jika ingin lebih banyak meletakkan state, gunakan objek sebagai state:

Store another route in main route

import { Elysia } from "elysia";
 
// plugin: elysia-plugin
const plugin = new Elysia()
  .state("version", "2.0.0")
  .decorate("getNewDate", () => Date.now() + 1000)
  .get("/elysia-plugin", () => "Hi");
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .use(plugin) // Use the plugin
  .get("/tracks", ({ store, getDate, getNewDate }) => {
    console.log("Store version:", store.version);
    console.log("Current date:", getDate());
    console.log("New date:", getNewDate());
    return {
      tracks: [
        { id: 1, title: "Track 1" },
        { id: 2, title: "Track 2" },
        { id: 3, title: "Track 3" },
      ],
      version: store.version,
      date: getDate(),
      newDate: getNewDate(),
    };
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Jika kita menambahkan route dari luar main route, kita bisa menggunakan use untuk menggunakannya sebagai plugin. Dalam contoh ini, kita membuat plugin yang berisi state dan fungsi dekorasi, lalu menggunakannya di main route.
  • Dengan cara ini, kita bisa memisahkan kode menjadi beberapa file dan menggunakannya sebagai plugin yang bisa digunakan di seluruh aplikasi.
  • Berikut adalah contoh respons JSON yang dihasilkan oleh route /tracks:
{
  "tracks": [
    {
      "id": 1,
      "title": "Track 1"
    },
    {
      "id": 2,
      "title": "Track 2"
    },
    {
      "id": 3,
      "title": "Track 3"
    }
  ],
  "version": "3.0.0", // versi dari state
  "date": 1752601981161,
  "newDate": 1752601982161
}

Import plugin dari file lain:

import Elysia from "elysia";
 
export const plugin = new Elysia()
  .state("version", "3.0.0")
  .decorate("getNewDate", () => Date.now() + 1000)
  .get("/elysia-plugin", () => "Hi");
import { Elysia } from "elysia";
import { plugin } from "./plugin";
 
const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .use(plugin) // Use the plugin
  .get("/tracks", ({ store, getDate, getNewDate }) => {
    console.log("Store version:", store.version);
    console.log("Current date:", getDate());
    console.log("New date:", getNewDate());
    return {
      tracks: [
        { id: 1, title: "Track 1" },
        { id: 2, title: "Track 2" },
        { id: 3, title: "Track 3" },
      ],
      version: store.version,
      date: getDate(),
      newDate: getNewDate(),
    };
  })
  .listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);

Grouping Routes

import { Elysia } from "elysia";
 
const app = new Elysia();
 
app.group("/v1", (app) =>
  app
    .get("/", () => "Hello Elysia v1")
    .get("/users", () => "List of users in v1")
    .get("/user/:id", ({ params: { id } }) => id),
);
 
app.listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Dengan menggunakan group, kita bisa mengelompokkan route berdasarkan versi atau kategori tertentu. Dalam contoh ini, kita mengelompokkan route dengan prefix /v1.
  • Untuk mengakses route yang dikelompokkan, kita bisa menggunakan URL seperti http://localhost:3000/v1/users atau http://localhost:3000/v1/user/1.

Another example:

import { Elysia } from "elysia";
 
const app = new Elysia();
 
app.group("/v1", (app) =>
  app
    .get("/", () => "Hello Elysia v1")
    .get("/users", () => "List of users in v1")
    .get("/user/:id", ({ params: { id } }) => id)
    .group("/products", (app) =>
      app
        .get("/", () => "List of products")
        .get("/:id", ({ params: { id } }) => `Product ID: ${id}`)
        .post(
          "/",
          ({ body }: { body: any }) =>
            `Created product with name: ${body.name}`,
        )
        .put(
          "/:id",
          ({
            params: { id },
            body,
          }: {
            params: { id: string };
            body: { name: string };
          }) => `Updated product ${id} with name: ${body.name}`,
        )
        .delete(
          "/:id",
          ({ params: { id } }) => `Deleted product with ID: ${id}`,
        ),
    ),
);
 
app.listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Dalam contoh ini, kita mengelompokkan route produk di dalam grup /v1. Kita bisa membuat route untuk mendapatkan daftar produk, mendapatkan produk berdasarkan ID, membuat produk baru, memperbarui produk, dan menghapus produk.
  • Dengan cara ini, kita bisa mengorganisir route dengan lebih baik dan membuatnya lebih mudah untuk dikelola.
  • unutk put, delete kita bisa menggunakan params untuk mendapatkan ID dari URL dan body untuk mendapatkan data yang dikirimkan dalam permintaan.

Body Validation

import { Elysia, t } from "elysia";
 
const app = new Elysia();
 
app.post(
  "/v1/user",
  ({ body }) => {
    return body;
  },
  {
    body: t.Object({
      name: t.String(),
      age: t.Number(),
    }),
    response: {
      name: t.String(),
      age: t.String(),
    },
  },
);
 
app.listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Dengan menggunakan t.Object, kita bisa melakukan validasi pada body permintaan. Dalam contoh ini, kita memvalidasi bahwa body harus berupa objek dengan properti name (string) dan age (number).
  • jika body tidak sesuai dengan skema yang ditentukan, Elysia akan mengembalikan respons error secara otomatis.
  • Dan juga bisa mengatur skema respons yang diharapkan dengan menggunakan response pada opsi route. Dalam contoh ini, kita mengatur bahwa respons harus berupa objek dengan properti name (string) dan age (string).
import { t } from "elysia";
 
export const signinDTO = t.Object({
  name: t.String(),
  age: t.Number(),
});
import { Elysia } from "elysia";
import { signinDTO } from "./models";
 
const app = new Elysia();
 
app.post(
  "/v1/user",
  ({ body }) => {
    return body;
  },
  {
    body: signinDTO,
    response: signinDTO,
  },
);
 
app.listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Kita bisa memisahkan skema validasi ke dalam file terpisah untuk menjaga kebersihan kode. Dalam contoh ini, kita membuat file models.ts yang berisi skema validasi untuk permintaan sign-in. [[#Create Project]]

Params Validation

import { Elysia, t } from "elysia";
import { signinDTO } from "./models";
 
const app = new Elysia();
 
app
  .post(
    "/v1/user",
    ({ body }) => {
      return body;
    },
    {
      body: signinDTO,
      response: signinDTO,
    },
  )
  .get(
    "/v1/user/:id",
    ({ params: { id } }) => {
      return id;
    },
    {
      params: t.Object({
        id: t.Numeric(),
      }),
    },
  );
 
app.listen(3000);
 
console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
);
  • Kita juga bisa melakukan validasi pada parameter URL dengan menggunakan t.Object. Dalam contoh ini, kita memvalidasi bahwa parameter id harus berupa angka pada route /v1/user/:id.
  • Jika parameter tidak sesuai dengan skema yang ditentukan, Elysia akan mengembalikan respons error secara otomatis.

Frontend Project

$ bun create vite react-app
$ cd react-app
$ bun install
$ bun run dev
  "scripts": {
    "dev": "bunx --bun vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  • Dengan menggunakan bun create vite, kita bisa membuat proyek frontend dengan Vite dan React. Setelah itu, kita bisa menginstal dependensi dengan bun install dan menjalankan server pengembangan dengan bun run dev.
  • Ubah package.json untuk menggunakan bunx agar bisa menjalankan bun dengan benar di dalam proyek Vite.
Graph Not Found

Table of Content

  • Pendahuluan
    • Fitur Utama Bun
    • perbedaan antara Bun dan Node.js
    • Install bun
  • Menggunakan bun
    • Create Project
    • Setup Web Server
    • Install Package
    • Development Server
    • Routing
    • Throw Error
      • Custom Error Response
    • Read File
    • Framework Rest API (elysia)
      • Make Route
      • Post Request
      • Get all routes
      • Context
      • Return JSON Response
      • State dan Decorate
      • Store another route in main route
      • Grouping Routes
      • Body Validation
      • Params Validation
    • Frontend Project
Ctrl+Vtoggle sidebar