Apidog

Nền tảng phát triển API hợp tác tất cả trong một

Thiết kế API

Tài liệu API

Gỡ lỗi API

Giả lập API

Kiểm thử API tự động

Hướng Dẫn Bắt Đầu Với Bun API

Mark Ponomarev

Mark Ponomarev

Updated on tháng 5 4, 2025

Trong số các công cụ phát triển gần đây thú vị nhất có Bun, một bộ công cụ JavaScript tất cả trong một cực kỳ nhanh chóng, được thiết kế để nâng cao năng suất của nhà phát triển và hiệu suất ứng dụng. Bun không chỉ là một runtime khác; nó là một hệ sinh thái gắn kết bao gồm runtime, bundler, test runner, package manager, và nhiều thứ khác, tất cả trong một tệp thực thi gốc duy nhất. Hướng dẫn này sẽ đưa bạn qua các kiến thức cơ bản về Bun, tập trung vào các khái niệm cốt lõi và các API mạnh mẽ của nó.

💡
Bạn muốn một công cụ Kiểm thử API tuyệt vời có thể tạo Tài liệu API đẹp mắt?

Bạn muốn một nền tảng tích hợp, Tất cả trong Một cho Đội ngũ Phát triển của bạn làm việc cùng nhau với năng suất tối đa?

Apidog đáp ứng mọi nhu cầu của bạn, và thay thế Postman với mức giá phải chăng hơn nhiều!
nút

Bun là gì?

Về cốt lõi, Bun được xây dựng dựa trên một công cụ runtime JavaScript hiệu suất cao. Khác với Node.js sử dụng công cụ V8 của Google, Bun sử dụng JavaScriptCore (JSC) của Apple, công cụ cung cấp sức mạnh cho Safari. Lựa chọn này, kết hợp với việc Bun được triển khai bằng ngôn ngữ lập trình cấp thấp Zig, đóng góp đáng kể vào tốc độ đáng kinh ngạc của nó. Thời gian khởi động cho các ứng dụng Bun thường được đo lường là nhanh hơn đáng kể so với các ứng dụng Node.js tương đương, đôi khi nhanh hơn gấp bốn lần hoặc hơn. Lợi thế về tốc độ này không chỉ dừng lại ở việc khởi động; các triển khai nội bộ của Bun đối với nhiều API khác nhau được tối ưu hóa để đạt thông lượng tối đa và sử dụng bộ nhớ tối thiểu.

Nhưng tham vọng của Bun còn vượt xa việc chỉ là một runtime nhanh hơn. Nó đặt mục tiêu trở thành một bộ công cụ toàn diện, giải quyết trực tiếp các nhu cầu phổ biến của nhà phát triển:

  1. Công cụ tích hợp: Lệnh bun tự nó đóng vai trò là điểm truy cập cho nhiều chức năng. bun run thực thi các script được định nghĩa trong package.json, bun install quản lý các dependency (thường nhanh hơn nhiều so với npm hoặc yarn), bun test chạy các bài kiểm thử sử dụng API tương thích với Jest, bun build đóng gói mã nguồn cho môi trường production, và bunx thực thi các package mà không cần cài đặt rõ ràng. Sự tích hợp này đơn giản hóa quy trình làm việc bằng cách giảm số lượng các công cụ phát triển riêng biệt cần thiết.
  2. Hỗ trợ TypeScript và JSX gốc: Một trong những tính năng nổi bật của Bun là khả năng hỗ trợ sẵn có cho các tệp TypeScript (.ts, .tsx) và JSX (.jsx). Bun bao gồm một bộ chuyển đổi (transpiler) tích hợp, được tối ưu hóa cao, xử lý các loại tệp này một cách liền mạch trong quá trình thực thi hoặc đóng gói. Điều này loại bỏ nhu cầu về các bước biên dịch riêng biệt liên quan đến tsc hoặc Babel cho nhiều tình huống phát triển phổ biến, giúp đơn giản hóa quy trình thiết lập.
  3. Khả năng tương thích hệ thống module: Bun đón nhận JavaScript hiện đại với sự hỗ trợ hạng nhất cho ES Modules (ESM). Tuy nhiên, nó cũng nhận ra hệ sinh thái rộng lớn hiện có được xây dựng trên CommonJS (CJS). Bun cung cấp khả năng tương thích mạnh mẽ cho cả hai hệ thống module, cho phép các nhà phát triển sử dụng importrequire phần lớn có thể hoán đổi cho nhau và tận dụng hàng triệu package CJS hiện có trên npm.
  4. Tuân thủ API Chuẩn Web: Một nguyên tắc thiết kế chính là việc triển khai các API Chuẩn Web. Các hàm và đối tượng như fetch, Request, Response, Headers, WebSocket, và Streams API (ReadableStream, WritableStream) được tích hợp sẵn vào phạm vi toàn cục của Bun. Điều này thúc đẩy khả năng di chuyển mã nguồn giữa các môi trường Bun phía máy chủ, frontend trình duyệt và các nền tảng điện toán biên, cho phép các nhà phát triển tái sử dụng các API quen thuộc trên các ngữ cảnh khác nhau.
  5. Khả năng tương thích với Node.js: Mặc dù đi theo con đường riêng với các API được tối ưu hóa và chuẩn Web, Bun đặt mục tiêu tương thích cao với bề mặt API của Node.js. Nhiều module Node.js tích hợp sẵn (node:fs, node:path, node:os, node:events, v.v.) và các đối tượng toàn cục (process, Buffer, __filename, __dirname) được triển khai một phần hoặc đầy đủ. Mục tiêu là cho phép nhiều dự án Node.js và package npm hiện có chạy trên Bun với ít hoặc không cần sửa đổi, định vị Bun như một "sự thay thế trực tiếp" tiềm năng trong nhiều trường hợp.

Bằng cách kết hợp các yếu tố này, Bun mang đến một giải pháp thay thế hấp dẫn cho các nhà phát triển JavaScript và TypeScript đang tìm kiếm hiệu suất, sự đơn giản và trải nghiệm phát triển hiện đại.

Cách cài đặt Bun

Bắt đầu với Bun được thiết kế để nhanh chóng và đơn giản trên nhiều nền tảng khác nhau. Phương pháp phổ biến nhất cho macOS, Linux và Windows Subsystem for Linux (WSL) sử dụng lệnh curl đơn giản được thực thi trong terminal của bạn:

curl -fsSL https://bun.sh/install | bash

Lệnh này tải xuống một script cài đặt và chuyển trực tiếp nó đến bash. Script xử lý việc phát hiện hệ điều hành và kiến trúc của bạn, tải xuống tệp thực thi Bun phù hợp và thường cài đặt nó vào ~/.bun/bin. Nó cũng cố gắng cập nhật tệp cấu hình shell của bạn (như .zshrc, .bashrc, hoặc .bash_profile) để thêm ~/.bun/bin vào PATH hệ thống của bạn, làm cho lệnh bun có sẵn trên toàn cục. Bạn có thể cần khởi động lại phiên terminal hoặc tự nguồn tệp cấu hình shell của mình (ví dụ: source ~/.zshrc) để các thay đổi có hiệu lực ngay lập tức.

Nếu bạn gặp vấn đề về quyền hoặc không muốn chuyển trực tiếp đến bash, bạn có thể tải script xuống trước và kiểm tra nó trước khi chạy:

curl -fsSL https://bun.sh/install -o install.sh
# Inspect install.sh if desired
bash install.sh

Các Phương pháp Cài đặt Khác:

  • NPM: Mặc dù chủ yếu được coi là một công cụ độc lập, bạn cũng có thể cài đặt Bun toàn cục thông qua npm, điều này có thể tiện lợi trong các môi trường đã có sẵn Node.js và npm:
npm install -g bun
  • Docker: Các image Docker chính thức của Bun có sẵn trên Docker Hub, cung cấp môi trường cô lập với Bun được cài đặt sẵn. Chúng hữu ích cho quy trình làm việc phát triển và triển khai bằng container. Bạn có thể tìm thấy nhiều image khác nhau dựa trên các bản phân phối hệ điều hành cơ bản (như Debian, Alpine) và các tag tương ứng với các phiên bản Bun cụ thể.
docker run --rm --init --ulimit memlock=-1:-1 oven/bun:latest bun --version
  • Windows: Hỗ trợ Windows gốc cho Bun vẫn được coi là thử nghiệm nhưng đang được phát triển tích cực. Cách được khuyến nghị để sử dụng Bun trên Windows hiện nay là thông qua WSL. Tuy nhiên, các bản dựng Windows trực tiếp đang dần có sẵn, và quy trình cài đặt có thể bao gồm việc tải xuống tệp `.zip` và thêm vị trí của tệp thực thi vào `PATH` hệ thống một cách thủ công. Hãy kiểm tra tài liệu chính thức của Bun để biết trạng thái mới nhất và hướng dẫn cài đặt Windows gốc.
  • Homebrew (macOS): Nếu bạn sử dụng Homebrew trên macOS, bạn có thể cài đặt Bun thông qua tap của nó:
brew tap oven-sh/bun
brew install bun

Xác minh:

Sau khi cài đặt, mở một cửa sổ terminal mới và xác minh việc cài đặt bằng cách kiểm tra phiên bản:

bun --version

Điều này sẽ hiển thị số phiên bản đã cài đặt, xác nhận rằng Bun đã sẵn sàng sử dụng. Bạn cũng có thể chạy bun --help để xem danh sách các lệnh và tùy chọn có sẵn.

Chạy Bun lần đầu tiên

Hãy cùng đi sâu vào việc viết và chạy một chương trình đơn giản với Bun. Một trong những tác vụ phổ biến nhất là tạo một máy chủ HTTP. Bun cung cấp một API tích hợp sẵn, được tối ưu hóa cao cho mục đích này: Bun.serve.

Tạo một tệp mới có tên server.js (hoặc server.ts, vì Bun hỗ trợ cả hai):

// server.ts

// Bun.serve khởi động máy chủ HTTP
const server = Bun.serve({
  // Chỉ định cổng để lắng nghe.
  // Mặc định là process.env.PORT || 3000
  port: 3000,

  // Hàm 'fetch' là trình xử lý yêu cầu cốt lõi.
  // Nó nhận một đối tượng Request chuẩn và phải trả về một đối tượng Response (hoặc một Promise giải quyết thành một đối tượng Response).
  fetch(request: Request): Response {
    // Tạo một đối tượng Response theo chuẩn Web API
    return new Response("Welcome to Bun!");
  },
});

// Ghi lại thông báo cho biết máy chủ đang chạy
console.log(`Listening on http://localhost:${server.port}`);

Đoạn mã này thực hiện các điều sau:

  1. Nó gọi Bun.serve, hàm chính để tạo máy chủ HTTP trong Bun.
  2. Nó truyền một đối tượng cấu hình, chỉ định port (trong trường hợp này là 3000).
  3. Phần quan trọng là hàm fetch. Hàm này được gọi cho mỗi yêu cầu HTTP đến. Nó tuân theo mẫu trình xử lý sự kiện fetch của Service Worker, chấp nhận một đối tượng Request chuẩn.
  4. Bên trong fetch, chúng ta xây dựng và trả về một đối tượng Response chuẩn. Ở đây, chúng ta chỉ đơn giản trả về văn bản thuần túy "Welcome to Bun!".
  5. Cuối cùng, chúng ta ghi lại một thông báo xác nhận vào console, bao gồm cả cổng thực tế mà máy chủ đang lắng nghe (có thể truy cập thông qua server.port).

Để chạy máy chủ này, mở terminal của bạn trong thư mục nơi bạn đã lưu tệp và thực thi:

bun run server.ts

Hoặc, nếu bạn lưu nó dưới dạng server.js:

bun run server.js

Bun sẽ thực thi script. Nếu bạn sử dụng TypeScript (server.ts), bộ chuyển đổi nội bộ của Bun sẽ xử lý việc chuyển đổi sang JavaScript ngay lập tức trước khi thực thi. Bạn sẽ thấy thông báo "Listening on http://localhost:3000".

Bây giờ, mở trình duyệt web của bạn và điều hướng đến http://localhost:3000. Bạn sẽ thấy văn bản "Welcome to Bun!" được hiển thị.

Để dừng máy chủ, quay lại terminal của bạn và nhấn Ctrl + C.

Ví dụ đơn giản này minh họa sự dễ dàng trong việc thiết lập một máy chủ cơ bản và chạy mã nguồn (bao gồm cả TypeScript) trực tiếp với Bun, thể hiện tính tích hợp của nó và sự phụ thuộc vào các API Chuẩn Web như RequestResponse.

Hỗ trợ TypeScript gốc trong Bun là gì?

Một trong những lợi thế đáng kể nhất của Bun, đặc biệt đối với các nhà phát triển đã sử dụng hoặc muốn áp dụng TypeScript, là khả năng hỗ trợ hạng nhất, sẵn có. Không giống như Node.js, nơi việc chạy TypeScript thường yêu cầu biên dịch trước bằng trình biên dịch TypeScript (tsc) hoặc sử dụng các bộ tải/đăng ký như ts-node hoặc tsx, Bun xử lý nó một cách gốc và minh bạch.

Cách hoạt động:

Khi bạn yêu cầu Bun chạy một tệp .ts hoặc .tsx (ví dụ: bun run myscript.ts), nó sẽ tự động gọi bộ chuyển đổi nội bộ của nó. Bộ chuyển đổi này được viết bằng mã gốc (Zig) và cực kỳ nhanh. Công việc của nó là:

  1. Loại bỏ kiểu dữ liệu: Loại bỏ các chú thích kiểu dữ liệu TypeScript, interface, enum, v.v., vì chúng không phải là một phần của việc thực thi JavaScript chuẩn.
  2. Chuyển đổi cú pháp: Chuyển đổi cú pháp dành riêng cho TypeScript (như cách sử dụng enum nhất định hoặc cú pháp decorator cũ hơn nếu được cấu hình) thành JavaScript tương đương.
  3. Xử lý JSX: Chuyển đổi cú pháp JSX (được sử dụng trong các tệp `.tsx` và `.jsx`) thành các lệnh gọi hàm JavaScript chuẩn (thường là `React.createElement` hoặc một runtime JSX tương đương được cấu hình).

Lợi ích chính là điều này xảy ra ngay lập tức trong quá trình thực thi (bun run) hoặc đóng gói (bun build). Không có bước build riêng biệt, rõ ràng nào được yêu cầu chỉ để chạy mã TypeScript của bạn trong quá trình phát triển.

Ví dụ:

Xem xét tệp TypeScript này (greet.ts):

// greet.ts
interface User {
  name: string;
  id: number;
}

function greetUser(user: User): void {
  console.log(`Hello, ${user.name} (ID: ${user.id})!`);
}

const myUser: User = { name: "Bun Developer", id: 123 };

greetUser(myUser);

// You can even use modern features Bun supports
const message = `Bun version: ${Bun.version}`;
console.log(message);

Bạn có thể chạy trực tiếp điều này:

bun run greet.ts

Bun sẽ chuyển đổi nó nội bộ và thực thi JavaScript kết quả, tạo ra đầu ra như sau:

Hello, Bun Developer (ID: 123)!
Bun version: 1.x.y

Hỗ trợ JSX:

Tương tự, nếu bạn có một tệp .tsx với JSX:

// component.tsx

// Assuming you have JSX configured (Bun defaults often work with React)
function MyComponent({ name }: { name: string }) {
  return <div className="greeting">Hello, {name}!</div>;
}

// NOTE: Running this directly won't render HTML,
// it just shows the transpiled JS structure.
// You'd typically use this within a larger app or framework.
console.log("Simulating component creation (transpiled output structure):");
// The actual output depends on JSX transform settings,
// but it would be JavaScript objects/function calls.

Chạy bun run component.tsx sẽ thực thi tệp, chuyển đổi JSX thành JavaScript.

Cấu hình (`tsconfig.json`):

Bun tuân theo các tệp tsconfig.json cho các tùy chọn cấu hình ảnh hưởng đến quá trình chuyển đổi. Mặc dù nó không thực hiện kiểm tra kiểu dữ liệu đầy đủ như tsc (Bun tập trung vào tốc độ *thực thi* và *chuyển đổi*), nó đọc tsconfig.json để hiểu các cài đặt như:

  • jsx: ("react-jsx", "react", v.v.) Cách JSX nên được chuyển đổi.
  • jsxImportSource: Module để import các hàm trợ giúp JSX từ (ví dụ: "react").
  • experimentalDecorators, emitDecoratorMetadata: Hỗ trợ cho decorators.
  • paths, baseUrl: Ánh xạ đường dẫn module cho các bí danh import tùy chỉnh.
  • target, module: Mặc dù Bun quản lý việc thực thi, những thứ này đôi khi có thể ảnh hưởng đến các chi tiết chuyển đổi nhỏ.
  • strict, strictNullChecks, v.v.: Những thứ này chủ yếu ảnh hưởng đến việc *kiểm tra* kiểu dữ liệu (mà Bun không thực hiện trong quá trình run), nhưng một số hành vi phát ra JavaScript liên quan có thể bị ảnh hưởng.

Nếu không tìm thấy tệp tsconfig.json, Bun sẽ sử dụng các cài đặt mặc định hợp lý.

Sự tích hợp liền mạch này giúp việc bắt đầu các dự án TypeScript với Bun trở nên cực kỳ đơn giản và nhanh chóng, giảm rào cản gia nhập và tăng tốc chu kỳ phát triển.

Tìm hiểu về các API dành riêng cho Bun

Mặc dù Bun rất chú trọng đến khả năng tương thích với các API Chuẩn Web và Node.js, nó cũng giới thiệu bộ API tích hợp sẵn, được tối ưu hóa riêng dưới đối tượng toàn cục Bun. Các API này thường cung cấp các lựa chọn thay thế hiệu suất cao hoặc các wrapper tiện lợi cho các tác vụ phổ biến tận dụng khả năng mã gốc của Bun.

Dưới đây là cái nhìn sơ lược về một số API Bun.* chính:

  • Bun.serve({...}): Như đã thấy trong Hướng dẫn nhanh, đây là nền tảng để xây dựng các máy chủ HTTP và WebSocket có hiệu suất cao. Nó cung cấp cấu hình đơn giản và sử dụng chữ ký trình xử lý fetch chuẩn. (Sẽ được trình bày chi tiết sau).
  • Bun.file(path): Tạo một đối tượng BunFile, là một tham chiếu lười biếng đến một tệp trên đĩa. Nó cung cấp các phương thức được tối ưu hóa cao để đọc nội dung tệp ở nhiều định dạng khác nhau (.text(), .json(), .arrayBuffer(), .stream()) chỉ khi cần thiết. Điều này thường nhanh hơn nhiều so với các phương thức tương đương trong node:fs.
  • Bun.write(path, data): Đối tác của Bun.file, được sử dụng để ghi dữ liệu vào tệp một cách hiệu quả. Nó chấp nhận nhiều loại dữ liệu khác nhau (chuỗi, Blobs, Buffers, các BunFile khác) và thực hiện ghi nguyên tử theo mặc định.
  • Bun.build({...}): Cung cấp quyền truy cập theo chương trình vào bundler tích hợp của Bun, tương thích với API esbuild. Cho phép đóng gói JavaScript/TypeScript cho trình duyệt hoặc các runtime khác trực tiếp từ bên trong các script Bun.
  • Bun.spawn({...}) / Bun.spawnSync({...}): Chạy các lệnh bên ngoài dưới dạng các tiến trình con, tương tự như child_process của Node.js. Cung cấp các API streaming bất đồng bộ và một phiên bản đồng bộ đơn giản hơn, được tối ưu hóa cho chi phí thấp.
  • Bun.Transpiler({...}): Truy cập trực tiếp theo chương trình vào bộ chuyển đổi nội bộ nhanh chóng của Bun để chuyển đổi TypeScript/JSX sang JavaScript mà không cần đóng gói đầy đủ.
  • Bun.password.hash(...) / Bun.password.verify(...): Các hàm an toàn và dễ sử dụng để băm (hash) và xác minh mật khẩu sử dụng các thuật toán chuẩn công nghiệp như Argon2id (được khuyến nghị) và bcrypt. Tránh cần các thư viện bên ngoài.
  • Bun.env: Một đối tượng cung cấp quyền truy cập vào các biến môi trường, tương tự như process.env, nhưng có thể cung cấp quyền truy cập nhanh hơn trong một số trường hợp.
  • Bun.version: Một chuỗi chứa phiên bản Bun hiện đang chạy.
  • Bun.revision: Một chuỗi chứa mã băm commit Git của bản dựng Bun hiện đang chạy.
  • Bun.sleep(ms) / Bun.sleepSync(ms): Các hàm để tạm dừng thực thi trong một khoảng thời gian được chỉ định.
  • Bun.gc(): Kích hoạt thu gom rác thủ công (sử dụng hạn chế, chủ yếu cho gỡ lỗi/đo hiệu năng).
  • Bun.resolveSync(specifier, parentPath) / Bun.resolve(specifier, parentPath): Thực hiện phân giải module theo kiểu Node.js theo chương trình để tìm đường dẫn tuyệt đối của một module.

Các API này thể hiện nỗ lực của Bun nhằm cung cấp các giải pháp tích hợp sẵn, được tối ưu hóa cho các tác vụ phát triển phổ biến, giảm sự phụ thuộc vào các dependency bên ngoài và tận dụng tốc độ của lõi gốc.

Các API Web trong Bun

Một lựa chọn thiết kế cơ bản trong Bun là cam kết mạnh mẽ của nó trong việc triển khai các API Chuẩn Web. Bất cứ khi nào một API chuẩn tồn tại cho một tác vụ cụ thể (đặc biệt là cho mạng và xử lý dữ liệu), Bun ưu tiên triển khai chuẩn đó thay vì phát minh ra một API độc quyền hoặc chỉ dựa vào các quy ước của Node.js.

Cách tiếp cận này mang lại một số lợi thế đáng kể:

  1. Khả năng di chuyển mã nguồn: Mã nguồn được viết bằng các API Chuẩn Web thường có thể được tái sử dụng trên các môi trường JavaScript khác nhau – trình duyệt, Node.js (cũng đang ngày càng áp dụng các chuẩn Web), Deno, Cloudflare Workers và Bun – với ít sửa đổi hơn.
  2. Sự quen thuộc: Các nhà phát triển đã quen thuộc với các API trình duyệt có thể tận dụng kiến thức đó khi làm việc với Bun, giảm đường cong học tập.
  3. Khả năng chống lỗi thời: Việc tuân thủ các chuẩn được thiết lập bởi các tổ chức như WHATWG và W3C thường dẫn đến các API ổn định hơn và được hỗ trợ rộng rãi hơn về lâu dài.
  4. Hiệu suất: Các triển khai gốc của Bun đối với các API Web này được tối ưu hóa cao cho runtime của nó.

Các API Chuẩn Web chính được triển khai trong Bun bao gồm:

Fetch API:

  • fetch(): Hàm toàn cục để thực hiện các yêu cầu HTTP(S).
  • Request: Đại diện cho một yêu cầu HTTP.
  • Response: Đại diện cho một phản hồi HTTP.
  • Headers: Đại diện cho các header HTTP.

URL API:

  • URL: Để phân tích cú pháp và thao tác với các URL.
  • URLSearchParams: Để làm việc với các tham số truy vấn URL.

Streams API:

  • ReadableStream: Đại diện cho một nguồn dữ liệu có thể đọc bất đồng bộ. Được sử dụng cho body yêu cầu/phản hồi, đọc tệp, v.v.
  • WritableStream: Đại diện cho một đích đến dữ liệu có thể ghi bất đồng bộ. Được sử dụng cho body yêu cầu, ghi tệp, v.v.
  • TransformStream: Một luồng hai chiều chuyển đổi dữ liệu khi nó đi qua (ví dụ: nén, mã hóa).

Encoding API:

  • TextEncoder: Mã hóa chuỗi thành Uint8Array (thường là UTF-8).
  • TextDecoder: Giải mã Uint8Array thành chuỗi.

Blob API:

  • Blob: Đại diện cho dữ liệu thô bất biến, thường được sử dụng cho các đối tượng giống tệp.
  • File: Mở rộng Blob để đại diện cho các tệp, bao gồm siêu dữ liệu như tên và ngày sửa đổi cuối cùng. (Thường được tạo thông qua Bun.file().slice() hoặc từ dữ liệu form).

FormData API:

  • FormData: Để xây dựng các tập hợp cặp khóa/giá trị, thường được sử dụng để gửi dữ liệu form trong các yêu cầu fetch.

WebSocket API:

  • WebSocket: API phía client để thiết lập các kết nối WebSocket. (Việc xử lý phía server được tích hợp vào Bun.serve).

Timers:

  • setTimeout, setInterval, clearTimeout, clearInterval: Các hàm chuẩn để lập lịch thực thi mã.

Console API:

  • console.log, console.error, console.warn, v.v.: Các hàm ghi nhật ký chuẩn.

Crypto API:

  • crypto.subtle: Cung cấp quyền truy cập vào các nguyên thủy mã hóa cấp thấp (băm, ký, mã hóa).
  • crypto.randomUUID(): Tạo UUID v4.
  • crypto.getRandomValues(): Tạo số ngẫu nhiên mạnh về mặt mật mã.

Performance API:

  • performance.now(): Cung cấp dấu thời gian độ phân giải cao để đo hiệu suất.

Bằng cách cung cấp các triển khai mạnh mẽ, hiệu suất cao cho các API Web thiết yếu này, Bun định vị mình là một runtime hiện đại rất phù hợp để xây dựng các máy chủ web, API và các ứng dụng tập trung vào mạng khác bằng cách sử dụng các giao diện quen thuộc, được chuẩn hóa.

Máy chủ HTTP của Bun, Giải thích

Cách chính để tạo máy chủ web trong Bun là thông qua API Bun.serve. Nó được thiết kế để có hiệu suất vượt trội và dễ sử dụng, tích hợp liền mạch với các API Chuẩn Web như Request, Responsefetch.

Các Khái niệm Cốt lõi:

Hàm Bun.serve nhận một đối tượng cấu hình và trả về một đối tượng Server. Phần quan trọng nhất của cấu hình là hàm fetch.

import { type Server } from "bun";

const server: Server = Bun.serve({
  port: 8080, // Cổng để lắng nghe
  hostname: "0.0.0.0", // Giao diện mạng để ràng buộc (0.0.0.0 cho tất cả)

  // fetch: Trái tim của máy chủ - xử lý các yêu cầu đến
  async fetch(req: Request, server: Server): Promise<Response> {
    // req là một đối tượng Request theo chuẩn Web API
    // server là tham chiếu đến chính instance Server

    const url = new URL(req.url);

    // Ví dụ định tuyến cơ bản
    if (url.pathname === "/") {
      return new Response("Homepage");
    }
    if (url.pathname === "/about") {
      return new Response("About Us page");
    }
    if (url.pathname === "/greet" && req.method === "GET") {
        const name = url.searchParams.get("name") || "World";
        return new Response(`Hello, ${name}!`);
    }
     if (url.pathname === "/data" && req.method === "POST") {
        try {
            const data = await req.json(); // Đọc body yêu cầu dưới dạng JSON
            console.log("Received data:", data);
            return new Response(JSON.stringify({ received: data }), {
               headers: { 'Content-Type': 'application/json' }
            });
        } catch (e) {
            return new Response("Invalid JSON body", { status: 400 });
        }
    }

    // Mặc định 404 Not Found
    return new Response("Page Not Found", { status: 404 });
  },

  // error: Trình xử lý tùy chọn cho các lỗi xảy ra *ngoài* trình xử lý fetch
  error(error: Error): Response | Promise<Response> {
    console.error("[Server Error]", error);
    return new Response("Something went wrong!", { status: 500 });
  },

  // development: Đặt thành true để hiển thị trang lỗi phát triển hữu ích (mặc định: !process.env.NODE_ENV=production)
   development: true,

   // Các tùy chọn khác như 'websocket', 'tls' tồn tại cho các trường hợp sử dụng nâng cao
});

console.log(`Bun server listening on http://${server.hostname}:${server.port}`);

// Bạn có thể tương tác với đối tượng server:
// server.stop() // Dừng máy chủ
// server.reload({...}) // Cập nhật cấu hình máy chủ (ví dụ: trình xử lý fetch) một cách động

Các Tính năng Chính:

  • Hiệu suất: Bun.serve được xây dựng trên việc triển khai máy chủ HTTP tùy chỉnh, được tối ưu hóa cao của Bun, viết bằng Zig. Nó có khả năng xử lý số lượng yêu cầu rất cao mỗi giây với độ trễ thấp và tiêu thụ tài nguyên ít hơn so với nhiều framework Node.js.
  • Trình xử lý fetch: Sử dụng chữ ký chuẩn (Request) => Response | Promise<Response> làm cho logic cốt lõi quen thuộc với bất kỳ ai đã làm việc với Service Workers, Cloudflare Workers hoặc các framework web hiện đại khác. Nó khuyến khích việc sử dụng các đối tượng RequestResponse chuẩn.
  • Đối tượng Request: Tham số req cung cấp quyền truy cập vào các thuộc tính và phương thức Request chuẩn: req.url, req.method, req.headers, req.json(), req.text(), req.arrayBuffer(), req.formData(), req.body (một ReadableStream).
  • Đối tượng Response: Bạn tạo và trả về các đối tượng Response chuẩn, cho phép bạn dễ dàng đặt body (chuỗi, Buffer, Blob, Stream, v.v.), mã trạng thái và header.
  • Xử lý lỗi: Hàm error tùy chọn cung cấp một nơi tập trung để bắt các lỗi xảy ra *trước* hoặc *trong* quá trình xử lý ban đầu của một yêu cầu bởi chính máy chủ (ví dụ: lỗi bắt tay TLS, yêu cầu bị định dạng sai), hoặc các lỗi được ném ra một cách đồng bộ *ngoài* khối try...catch của trình xử lý fetch. Các lỗi *trong* trình xử lý fetch bất đồng bộ thường nên được bắt ở đó.
  • Streaming: Body của cả yêu cầu và phản hồi có thể được stream bằng cách sử dụng các API ReadableStreamWritableStream chuẩn, điều này cần thiết để xử lý các tải lên hoặc tải xuống lớn một cách hiệu quả.
  • Tải lại động: Phương thức server.reload() cho phép cập nhật các tùy chọn máy chủ, bao gồm cả trình xử lý fetcherror, mà không cần khởi động lại máy chủ hoàn toàn, điều này hữu ích cho các thiết lập thay thế module nóng (HMR).

Bun.serve cung cấp một nền tảng mạnh mẽ nhưng đơn giản để xây dựng các ứng dụng web và API trong Bun, ưu tiên tốc độ và tuân thủ các chuẩn web.

Client Fetch của Bun

Bổ sung cho API máy chủ, Bun cung cấp hàm fetch toàn cục để thực hiện các yêu cầu HTTP(S) đi ra. Việc triển khai này tuân thủ chặt chẽ chuẩn WHATWG Fetch, làm cho nó quen thuộc với các nhà phát triển web và đảm bảo tính nhất quán với hàm fetch được sử dụng bên trong Bun.serve. Việc triển khai gốc của Bun đảm bảo hiệu suất cao cho các tác vụ mạng phía client.

Thực hiện yêu cầu:

Cách sử dụng cơ bản bao gồm việc gọi fetch với một URL và tùy chọn một đối tượng cấu hình:

async function makeRequests() {
  const url = "https://httpbin.org"; // Một dịch vụ hữu ích để kiểm thử các yêu cầu HTTP

  // --- Yêu cầu GET cơ bản ---
  console.log("--- GET Request ---");
  try {
    const getResponse = await fetch(`${url}/get?param1=value1`);

    console.log(`Status: ${getResponse.status} ${getResponse.statusText}`);

    // Kiểm tra xem yêu cầu có thành công không (trạng thái 200-299)
    if (!getResponse.ok) {
      throw new Error(`HTTP error! status: ${getResponse.status}`);
    }

    // Truy cập các header
    console.log("Content-Type Header:", getResponse.headers.get('content-type'));

    // Đọc body phản hồi dưới dạng JSON
    const getData = await getResponse.json();
    console.log("Response JSON:", getData.args); // httpbin.org/get trả về các tham số truy vấn trong 'args'

  } catch (error) {
    console.error("GET request failed:", error);
  }

  // --- Yêu cầu POST với body JSON ---
  console.log("\n--- POST Request (JSON) ---");
  try {
     const postData = { name: "Bun", type: "Runtime" };
     const postResponse = await fetch(`${url}/post`, {
        method: "POST",
        headers: {
          // Chỉ ra chúng ta đang gửi JSON
          "Content-Type": "application/json",
          "Accept": "application/json", // Chỉ ra chúng ta ưu tiên nhận lại JSON
          "X-Custom-Header": "BunFetchExample",
        },
        // Body phải được chuyển thành chuỗi cho JSON
        body: JSON.stringify(postData),
     });

     if (!postResponse.ok) {
        throw new Error(`HTTP error! status: ${postResponse.status}`);
     }

     const postResult = await postResponse.json();
     console.log("POST Response JSON:", postResult.json); // httpbin.org/post trả về JSON đã gửi trong 'json'

  } catch (error) {
     console.error("POST request failed:", error);
  }

   // --- Yêu cầu với Timeout ---
   console.log("\n--- GET Request with Timeout ---");
   try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), 2000); // Hủy sau 2 giây

      const timeoutResponse = await fetch(`${url}/delay/5`, { // Endpoint này chờ 5 giây
         signal: controller.signal // Truyền AbortSignal
      });

      clearTimeout(timeoutId); // Xóa timeout nếu fetch hoàn thành nhanh hơn
      console.log("Timeout request succeeded (unexpected for /delay/5)");

   } catch (error: any) {
      // AbortError được ném ra khi signal hủy
      if (error.name === 'AbortError') {
         console.log("Fetch bị hủy do timeout, như mong đợi.");
      } else {
         console.error("Timeout request failed:", error);
      }
   }
}

await makeRequests();

Các Tính năng và Tùy chọn Chính:

API Chuẩn: Sử dụng chữ ký quen thuộc fetch(url, options).

Phương thức: Hỗ trợ tất cả các phương thức HTTP chuẩn (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS). Được chỉ định thông qua tùy chọn method.

Header: Header được đặt bằng tùy chọn headers, có thể là một đối tượng Headers hoặc một đối tượng thuần túy đơn giản với cặp khóa-giá trị.

Body yêu cầu: Tùy chọn body chấp nhận nhiều loại khác nhau:

  • string: Được gửi dưới dạng văn bản (phổ biến cho JSON, XML, văn bản thuần túy).
  • ArrayBuffer / TypedArray: Được gửi dưới dạng dữ liệu nhị phân.
  • Blob / File: Được gửi dưới dạng dữ liệu nhị phân, thường với Content-Type phù hợp.
  • ReadableStream: Stream nội dung body, phù hợp cho các tải lên lớn.
  • URLSearchParams: Tự động đặt Content-Type thành application/x-www-form-urlencoded.
  • FormData: Tự động đặt Content-Type thành multipart/form-data.

Xử lý phản hồi: Lệnh gọi fetch trả về một Promise giải quyết thành một đối tượng Response.

  • response.ok: Boolean chỉ ra trạng thái HTTP thành công (200-299).
  • response.status: Mã trạng thái HTTP (ví dụ: 200, 404).
  • response.statusText: Thông báo trạng thái HTTP (ví dụ: "OK", "Not Found").
  • response.headers: Một đối tượng Headers để truy cập các header phản hồi.
  • Các phương thức đọc body: response.json(), response.text(), response.arrayBuffer(), response.blob(), response.formData(). Chúng trả về các Promise.
  • response.body: Một ReadableStream để stream body phản hồi.

Timeout & Hủy bỏ: Sử dụng AbortControllerAbortSignal để triển khai timeout hoặc hủy yêu cầu thủ công. Truyền signal vào các tùy chọn fetch.

Chuyển hướng: Được kiểm soát bởi tùy chọn redirect ('follow', 'manual', 'error'). Mặc định là 'follow'.

  • Caching: Được kiểm soát bởi tùy chọn cache (ví dụ: 'no-store', 'reload').

Hàm fetch của Bun cung cấp một cách hiệu suất cao và tuân thủ chuẩn để tương tác với các tài nguyên HTTP, tích hợp liền mạch vào hệ sinh thái Bun.

WebSockets trong Bun

WebSockets cung cấp một cách để thiết lập các kênh giao tiếp hai chiều, liên tục giữa client và server qua một kết nối TCP duy nhất. Bun cung cấp sự hỗ trợ tuyệt vời cho WebSockets, cả cho việc tạo client WebSocket và xử lý các kết nối WebSocket phía server trong Bun.serve.

1. Client WebSocket:

Bun triển khai API WebSocket chuẩn của trình duyệt để tạo các kết nối client.

// websocket-client.ts

const wsUrl = "wss://ws.postman-echo.com/raw"; // Máy chủ echo công cộng

console.log(`Connecting client to ${wsUrl}...`);

const socket = new WebSocket(wsUrl);

// Sự kiện: Kết nối được mở thành công
socket.addEventListener("open", (event) => {
  console.log("[Client] WebSocket connection established!");
  socket.send("Hello from Bun client!");

  // Gửi dữ liệu nhị phân sau một khoảng trễ ngắn
  setTimeout(() => {
     const binaryData = new TextEncoder().encode("Bun binary data");
     console.log("[Client] Sending binary:", binaryData);
     socket.send(binaryData);
  }, 500);

   // Đóng kết nối sau một thời gian
  setTimeout(() => {
     console.log("[Client] Closing connection...");
     socket.close(1000, "Client done testing"); // 1000 = Đóng bình thường
  }, 2000);
});

// Sự kiện: Nhận tin nhắn từ server
socket.addEventListener("message", async (event) => {
  // event.data có thể là chuỗi, Blob hoặc ArrayBuffer
  let messageContent: string | Uint8Array;
  if (typeof event.data === "string") {
    messageContent = event.data;
    console.log(`[Client] Received string: "${messageContent}"`);
  } else if (event.data instanceof Blob) {
    // Bun thường nhận dữ liệu nhị phân dưới dạng Blobs từ API WebSocket
    const arrayBuffer = await event.data.arrayBuffer();
    messageContent = new Uint8Array(arrayBuffer);
    console.log(`[Client] Received binary (Blob):`, messageContent, `(${new TextDecoder().decode(messageContent)})`);
  } else if (event.data instanceof ArrayBuffer) {
     messageContent = new Uint8Array(event.data);
     console.log(`[Client] Received binary (ArrayBuffer):`, messageContent, `(${new TextDecoder().decode(messageContent)})`);
  } else {
    console.log("[Client] Received unknown message type:", event.data);
  }
});

// Sự kiện: Đã xảy ra lỗi (vấn đề mạng, v.v.)
socket.addEventListener("error", (event) => {
  // Đối tượng sự kiện thường thiếu chi tiết cụ thể. Kiểm tra console/network logs.
  console.error("[Client] WebSocket error:", event.type);
});

// Sự kiện: Kết nối bị đóng
socket.addEventListener("close", (event) => {
  console.log(
    `[Client] WebSocket closed. Code: ${event.code}, Reason: "${event.reason}", Clean: ${event.wasClean}`
  );
  // Thoát script sạch sẽ sau khi socket bị đóng
  process.exit(0);
});

// Giữ cho script sống một chút để xử lý sự kiện, việc thoát được xử lý trong listener 'close'
// setInterval(() => {}, 1000);

Chạy mã này với bun run websocket-client.ts. Nó kết nối đến máy chủ echo, gửi tin nhắn, nhận lại và sau đó đóng kết nối.

2. Máy chủ WebSocket (Tích hợp Bun.serve):

Việc xử lý các kết nối WebSocket phía server được thực hiện bằng cách thêm thuộc tính websocket vào đối tượng cấu hình của Bun.serve. Thuộc tính này chứa các hàm xử lý cho các sự kiện vòng đời khác nhau của WebSocket.

// websocket-server.ts
import { type ServerWebSocket, type WebSocketHandler } from "bun";

console.log("Starting WebSocket server...");

// Định nghĩa đối tượng xử lý WebSocket
const wsHandler: WebSocketHandler<{ authToken: string }> = {
  // open(ws): Được gọi khi một kết nối WebSocket mới được thiết lập.
  // Lệnh gọi server.upgrade() trong 'fetch' là cần thiết trước.
  open(ws: ServerWebSocket<{ authToken: string }>) {
    console.log(`[Server] Connection opened. Token: ${ws.data.authToken}`);
    ws.send("Welcome to the Bun WebSocket server!");
    // Đăng ký một chủ đề pub/sub
    ws.subscribe("the-group-chat");
    // Xuất bản một tin nhắn đến chủ đề (bao gồm cả người dùng mới)
    ws.publish("the-group-chat", `User with token ${ws.data.authToken} joined.`);
  },

  // message(ws, message): Được gọi khi nhận được tin nhắn từ client.
  // 'message' có thể là chuỗi hoặc Uint8Array (Buffer).
  message(ws: ServerWebSocket<{ authToken: string }>, message: string | Buffer) {
    const messageString = message.toString(); // Xử lý cả chuỗi/buffer
    console.log(`[Server] Received from token ${ws.data.authToken}: ${messageString}`);

     // Echo lại tin nhắn với token được thêm vào trước
    // ws.send(`[${ws.data.authToken}] You sent: ${messageString}`);

    // Xuất bản tin nhắn đến mọi người trong phòng chat (bao gồm cả người gửi)
    server.publish("the-group-chat", `[${ws.data.authToken}] ${messageString}`);
  },

  // close(ws, code, reason): Được gọi khi một kết nối WebSocket bị đóng.
  close(ws: ServerWebSocket<{ authToken: string }>, code: number, reason: string) {
    const goodbyeMessage = `[Server] Connection closed. Token: ${ws.data.authToken}, Code: ${code}, Reason: ${reason}`;
    console.log(goodbyeMessage);
     // Hủy đăng ký và thông báo cho người khác
     ws.unsubscribe("the-group-chat");
     server.publish("the-group-chat", `User with token ${ws.data.authToken} left.`);
  },

  // drain(ws): Tùy chọn. Được gọi khi lượng dữ liệu đệm của socket giảm,
  // cho biết nó sẵn sàng nhận thêm dữ liệu sau khi có áp lực ngược.
  // Hữu ích để quản lý luồng khi gửi lượng lớn dữ liệu.
  // drain(ws: ServerWebSocket<{ authToken: string }>) {
  //   console.log(`[Server] Drain event for token ${ws.data.authToken}`);
  // },
};

// Tạo máy chủ HTTP với xử lý WebSocket
const server = Bun.serve<{ authToken: string }>({ // Truyền generic cho kiểu dữ liệu của ws.data
  port: 8081,

  // Trình xử lý fetch cần xử lý yêu cầu nâng cấp từ HTTP sang WebSocket
  fetch(req: Request, server: Server): Response | undefined {
    const url = new URL(req.url);
    // Kiểm tra xem yêu cầu có đang cố gắng nâng cấp lên WebSocket không
    if (url.pathname === "/ws") {
       // Trích xuất một số dữ liệu (ví dụ: auth token) để truyền vào ngữ cảnh WebSocket
       const token = req.headers.get("sec-websocket-protocol") || "anonymous"; // Ví dụ: sử dụng header protocol cho token
       const success = server.upgrade(req, {
         // Đối tượng data được gắn vào instance ws (ws.data)
         // có sẵn trong tất cả các trình xử lý websocket cho kết nối này.
         // PHẢI có thể chuyển đổi sang JSON nếu sử dụng trên các worker/process sau này.
         data: {
           authToken: token,
         },
         // Tùy chọn: Thêm các header vào phản hồi 101
         // headers: new Headers({ 'X-Custom-Response-Header': 'UpgradeValue' })
      });

      if (success) {
        // server.upgrade() xử lý phản hồi 101 Switching Protocols.
        // Không trả về gì từ trình xử lý fetch sau khi nâng cấp thành công.
        return undefined;
      } else {
         // Nâng cấp thất bại (ví dụ: header yêu cầu không hợp lệ)
         return new Response("WebSocket upgrade failed", { status: 400 });
      }
    }

    // Xử lý các yêu cầu HTTP thông thường trên các đường dẫn khác
    return new Response("Not a WebSocket endpoint. Try /ws", { status: 404 });
  },

  // Gắn đối tượng xử lý WebSocket
  websocket: wsHandler,

  error(error: Error): Response | Promise<Response> {
    console.error("[Server Error]", error);
    return new Response("Server error", { status: 500 });
  },
});

console.log(`Bun WebSocket server listening on ws://localhost:${server.port}/ws`);

Các Khái niệm Chính của Server:

Yêu cầu Nâng cấp: Các kết nối WebSocket bắt đầu như các yêu cầu HTTP GET chuẩn với các header cụ thể (Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key, v.v.). Trình xử lý fetch phải phát hiện các yêu cầu này.

  • server.upgrade(req, { data: ... }): Phương thức quan trọng này, được gọi trong trình xử lý fetch, cố gắng hoàn thành bắt tay WebSocket. Nếu thành công, nó trả về true, và Bun sẽ tiếp quản kết nối bằng cách sử dụng các trình xử lý websocket. Nếu thất bại, nó trả về false. Bạn phải trả về undefined từ fetch sau khi nâng cấp thành công.
  • Đối tượng xử lý websocket: Chứa các hàm open, message, closedrain để quản lý vòng đời WebSocket cho các client đã kết nối.
  • ServerWebSocket<T>: Kiểu đại diện cho một kết nối WebSocket phía server. Generic T định nghĩa kiểu của đối tượng ws.data.
  • ws.data: Một đối tượng được liên kết với mỗi kết nối, được khởi tạo bởi thuộc tính data trong các tùy chọn server.upgrade. Hữu ích để lưu trữ trạng thái dành riêng cho kết nối (như ID người dùng, token xác thực).

Pub/Sub: Bun bao gồm các khả năng xuất bản/đăng ký (publish/subscribe) tích hợp sẵn, hiệu quả cho WebSockets:

  • ws.subscribe("topic-name"): Đăng ký một kết nối vào một chủ đề.
  • ws.unsubscribe("topic-name"): Hủy đăng ký một kết nối.
  • ws.publish("topic-name", message): Gửi một tin nhắn đến tất cả các kết nối đã đăng ký chủ đề đó *trừ* người gửi ws.
  • server.publish("topic-name", message): Gửi một tin nhắn đến *tất cả* các kết nối đã đăng ký chủ đề đó (hữu ích cho việc phát sóng toàn hệ thống).

Áp lực ngược (Backpressure): Phương thức ws.send() trả về số byte đã được đệm. Nếu giá trị này tăng lên lớn, có thể bạn đang gửi dữ liệu nhanh hơn khả năng nhận của client. Sự kiện drain báo hiệu khi bộ đệm đã giảm kích thước, cho phép bạn tiếp tục gửi dữ liệu một cách an toàn.

Hỗ trợ WebSocket tích hợp của Bun cung cấp một cách hiệu suất cao và tiện lợi để xây dựng các tính năng thời gian thực vào ứng dụng, hoàn chỉnh với chức năng pub/sub tích hợp sẵn.


Hướng dẫn này đã trình bày các khía cạnh cơ bản của Bun, từ triết lý cốt lõi và cách cài đặt đến các API cụ thể như Bun.serve, Bun.file, Bun.build, và việc triển khai các Chuẩn Web quan trọng như fetchWebSocket. Bằng cách kết hợp tốc độ, công cụ tích hợp, hỗ trợ TypeScript/JSX gốc và tập trung vào các chuẩn, Bun mang đến một môi trường hấp dẫn và hiệu quả cho việc phát triển JavaScript và TypeScript hiện đại.

💡
Bạn muốn một công cụ Kiểm thử API tuyệt vời có thể tạo Tài liệu API đẹp mắt?

Bạn muốn một nền tảng tích hợp, Tất cả trong Một cho Đội ngũ Phát triển của bạn làm việc cùng nhau với năng suất tối đa?

Apidog đáp ứng mọi nhu cầu của bạn, và thay thế Postman với mức giá phải chăng hơn nhiều!
nút