如何部署 Bun 应用程序?

How to Deploy an Bun Application_
How to Deploy an Bun Application_

Bun 是一个 JavaScript 运行时,其设计宗旨是快速、轻量和易用。它由 Zig 编写,并由 JavaScriptCore(支持 Safari 的 JavaScript 引擎)提供支持。

Bun 内置了兼容 Node.js 的软件包管理器、测试运行器和捆绑程序。它还提供了一套高度优化的最小应用程序接口,用于执行启动 HTTP 服务器和写入文件等常见任务。

在本文中,您将使用 Bun 构建一个简单的 Web API,并使用 Back4app 容器将其部署到 Back4app 上。继续阅读,了解如何托管 Bun 应用程序。

Bun的优点

自 Bun 首次发布以来,甚至在 2023 年 9 月 V1 版本发布之前,Bun 在 JavaScript 社区就已经越来越受欢迎。原因如下

速度

Bun 是用 Zig 编写的,Zig 是一种低级编程语言,专为系统编程而设计,注重性能、安全性和可读性。旨在成为 C 和 C++ 的现代替代语言。

此外,与使用 Chrome 浏览器 V8 JavaScript 引擎的 Node.js 和 Deno 不同,它使用的是为 Safari 提供支持的JavaScriptCore 引擎

除了 Zig 和 JavaScript Core,Bun 还使用了许多其他技术,例如针对 JavaScript 进行优化的自定义内存分配器,以及在代码执行过程中进行优化的即时编译器(JIT)。

总之,与其他运行时相比,Zig、JavaScript Core 和其他优化技术的结合使 Bun 成为一个非常快速的 JavaScript 运行时。

Node.js 兼容性

Bun 被设计为 Node.js 的直接替代品,因此兼容所有 Node.js API。

它还拥有所有内置 Node.js 模块,如 crypto、fs、path 等。您可以在 Bun.js 文档中查看可用和不可用的 Node.js 模块。

此外,Bun 还是与 npm 兼容的软件包管理器。这意味着你可以使用 Bun 安装和管理 Node.js 软件包。

开箱即用的 TypeScript 支持

Bun 原生无缝支持 TypeScript,如果你喜欢或需要在项目中使用 TypeScript,它将是你的最佳选择。

TypeScript 是 JavaScript 的扩展和静态类型版本,它引入了高级语言特性和静态类型,以增强 JavaScript 开发。

使用 Bun,不需要额外的配置,也不需要额外的设置或构建程序来启用 TypeScript 功能。

Bun 的局限性

尽管 Bun 有很多优点,但在项目中使用之前,您需要考虑它的某些局限性。

资源有限

Bun 仍然相对较新,这意味着目前的社区规模较小。目前还没有很多专门针对 Bun-js 开发的资源,这意味着你可能很难弄清如何使用运行时。

不过,Bun 文档非常全面,可以作为宝贵的参考点。如果您遇到困难,也可以通过 Discord 频道寻求帮助。

支持 Windows

Bun 目前对 Windows 操作系统的支持有限。在撰写本报告时,Windows 系统仅支持运行时。

测试运行程序、软件包管理器和捆绑程序仍在开发中,因此无法在 Windows 上运行。请继续阅读,了解如何托管 Bun 应用程序。

制作Bun应用程序

在使用 Bun 之前,您必须先安装它。

要在 macOS、WSL 和 Linux 上安装 Bun,请运行以下命令:

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

设置开发环境

要创建新的 Bun 项目,请运行以下命令:

bun init

运行上述命令会在项目目录中初始化一个空的 Bun 项目。

在本教程中,您将使用 Elysia(最快的 Bun HTTP 服务器框架之一)构建一个简单的 API(根据基准测试)。

运行下面的命令安装该项目所需的 Elysia 和其他依赖项:

bun add elysia knex dotenv pg

命令中安装的其他依赖项包括

  • 查询生成器 Knex。您将使用这个依赖项来简化与数据库的交互。
  • dotenv,这个软件包可以帮助你管理项目中的环境变量。
  • pg(Postgres 数据库驱动程序),用于与 Postgres 数据库交互。

接下来,在项目中运行 knex 进行初始化:

knex init

上述命令会创建一个knexfile.js文件。该文件包含数据库的配置选项。

用下面的代码块替换文件中的代码:

require("dotenv").config();

export const development = {
  client: "pg",
  connection: process.env.DATABASE_URL,
  migrations: {
    directory: "./db/migrations",
  }
};

接下来,在项目根目录下创建一个db.js文件,并添加下面的代码块。

const knex = require("knex");
const knexFile = require("./knexfile.js");

const environment = process.env.NODE_ENV || "development";

export default knex(knexFile[environment]);

接下来,创建一个.env文件,并在文件中添加数据库连接详情或 URI。

例如

DATABASE_URL = <YOUR_DATABASE_URI>

用数据库 URL 替换 “YOUR_DATABASE_URI”。

注意:确保将.env文件添加到.gitignore文件中,以确保不会向版本控制提交私人信息。

创建数据库模型

要使用 Knex 创建数据库模型,需要创建一个迁移文件,并使用 Knex 编写创建 SQL 命令。

运行下面的命令创建第一次迁移:

knex migrate:make blog

接下来,用下面的代码块替换生成的迁移文件中的代码:

/**
 * @param { import("knex").Knex } knex
 * @returns { Promise<void> }
 */
export function up (knex) {
  return knex.schema.createTable("blog", (table) => {
    table.increments("id").primary();
    table.string("title").notNullable();
    table.string("content").notNullable();
    table.string("author").notNullable();
    table.timestamp("created_at").defaultTo(knex.fn.now());
  });
}

/**
 * @param { import("knex").Knex } knex
 * @returns { Promise<void> }
 */
export function down (knex) {
  return knex.schema.dropTable("blog");
}

最后,运行下面的代码块来执行迁移文件:

knex migrate:latest

上述命令会执行你之前生成的迁移文件,从而在数据库中创建一个博客表。

创建 Bun-Elysia 服务器

在这一步中,您将创建一个简单的 API 服务器。

打开index.ts文件,添加下面的代码块:

//index.ts
const { Elysia } = require("elysia");
let db = require("./db");

db = db.default;

const app = new Elysia();

在下面的代码块中,您导入了 Elysia 并创建了一个 Elysia 框架(应用程序)实例。

创建路由处理程序

接下来,您将为应用程序创建路由处理程序。您创建的处理程序将用于以下路由:

  • POST /posts/create-new-post
  • GET /posts
  • GET /posts/:id
  • PATCH /posts/:id/update-post
  • DELETE /posts/:id/delete-post

在您的index.ts文件中添加以下代码块,为”/posts/create-new-post “创建处理程序:

app.post("/posts/create-new-post", async (context) => {
  try {
    //Extract the title, content and author from the request body
    const { title, content, author } = context.body;

    //Insert the post into the database
    const post = await db("blog").insert({
      title,
      content,
      author,
    });

    //Send response to the client
    return new Response(JSON.stringify(post));
  } catch (error: any) {
    //Send error to the client
    return new Response(error.message, { status: 500 });
  }
});

上面的代码块是为您的端点添加新文章到数据库的路由处理程序。

将下面的代码块添加到您的index.ts文件中,创建一个处理程序,用于获取您的所有帖子”/posts”:

app.get("/posts", async (context) => {
  try {
    //Get all posts from the database
    const posts = await db.select("*").from("blog");

    //Send response to the client
    return new Response(JSON.stringify(posts));
  } catch (error: any) {
    //Send error to the client
    return new Response(error.message, { status: 500 });
  }
});

将下面的代码块添加到您的index.ts文件中,以创建一个处理程序,按 ID”/posts/:id “获取单个帖子:

app.get("/posts/:id", async (context) => {
  //Extract the id from the request params
  const { id } = context.params;

  //Get the post from the database
  const post = await db("blog").where({ id });

  //If the post is not found, send a 404 response
  if (post.length === 0) {
    return new Response("Post not found", { status: 404 });
  }

  //Send response to the client
  return new Response(JSON.stringify(post));
});

将下面的代码块添加到您的index.ts文件中,以创建一个处理程序,该处理程序通过 id”/posts/:id/update-post”,用有效载荷中的数据更新单个帖子:

app.patch("/posts/:id/update-post", async (context) => {
  //Extract the id from the request params
  const { id } = context.params;

  //Extract the title and content from the request body
  const { title, content } = context.body;

  //Update the post in the database
  const post = await db("blog").where({ id }).update(
    {
      title,
      content,
    },
    ["id", "title", "content"]
  );

  //Send response to the client
  return new Response(JSON.stringify(post));
});

在您的index.ts文件中添加以下代码块,创建一个处理程序,用于删除id 为”/posts/:id/delete-post “的单个帖子:

app.delete("/posts/:id/delete-post", async (context) => {
  //Extract the id from the request params
  const { id } = context.params;

  //Delete the post from the database
  const post = await db("blog").where({ id }).del();

  //Send response to the client
  return new Response(JSON.stringify(post));
});

最后,添加下面的代码块,为应用程序设置 PORT。

app.listen(3000, () => {
  console.log("Server running on port 3000");
});

运行下面的命令启动应用程序:

bun --watch index.ts

在 Back4app 容器上部署 Bun 应用程序

部署 Bun 应用程序需要几个步骤。

步骤 1:编写 Dockerfile

要创建 Dockerfile,请在终端上运行以下命令。

touch Dockerfile

运行上述命令会在项目根目录下创建一个 Dockerfile。

接下来,打开 Dockerfile 并添加下面的代码块:

FROM oven/bun

WORKDIR /app

COPY package.json .
COPY bun.lockb .

RUN bun install

COPY . .

EXPOSE 3000

CMD ["bun", "index.ts"]

在上面的 Dockerfile 中,第一行FROM oven/bun 指定了要使用的基础镜像。该镜像是预构建的镜像,包含 Bun 运行时及其所有依赖项。

下一行,WORKDIR /app,设置映像的工作目录。这是复制和运行应用程序代码的目录。

下面两行,即COPY package.json .COPY bun.lockb .,会将当前目录中的package.jsonbun.lockb文件复制到映像中。这些文件是 Bun 运行时安装应用程序依赖项所必需的。

下一行是RUN bun install,使用 Bun 运行时安装应用程序的依赖项。

下一行,COPY ...,将整个当前目录复制到映像中。这包括应用程序代码和任何其他必要文件。

下一行 “EXPOSE 3000“将容器的3000端口对外开放。这是应用程序将监听的端口。

最后一行CMD ["bun", "index.ts"]指定了容器启动时将运行的命令。该命令将启动 Bun 运行时并运行应用程序的index.ts文件。

最后,将代码推送到 GitHub

第 2 步:创建 Back4app 应用程序

托管 Bun 应用程序的下一步是在 Back4App 上创建一个新应用程序。首先,登录您的Back4App账户,如果您还没有账户,请注册。登录后,您将看到 Back4App 面板。

点击 “NEW APP “按钮,选择“容器即服务“选项。

Back4app BaaS vs CaaS

托管 Bun 应用程序的下一步是将您的 GitHub 账户连接到您的 Back4app 账户。连接账户后,Back4app 即可访问您账户上的资源库。

您可以决定授予账户中所有软件源或特定软件源的访问权限。选择要部署的应用程序,在本例中就是本教程中构建的应用程序,然后单击 “选择“。

新应用程序 back4app

单击 “选择“按钮后,系统将引导您进入配置页面,在这里您可以填写应用程序的详细信息,如端口和环境变量。

填写完详细信息后,单击 “创建应用程序“按钮。这将启动部署过程。部署应该会成功,您会得到一个访问应用程序的 URL,但如果失败,您可以利用 Back4app ChatGPT 集成来解决 Dockerfile 中的问题。

或者,您也可以使用 Back4app 的详细日志和故障排除指南手动排除部署错误。

结论

在本文中,您将了解 Bun JavaScript 运行时、它的优势和明显的局限性。您还将了解如何使用 Elysia、Knex 和 PostgreSQL 构建 Bun 应用程序。

最后,您将了解 Back4app 容器以及如何在该平台上部署 Bun 应用程序。

在使用 Bun 时,需要注意的是它仍处于早期阶段,未来可能会引入一些重大变化。

常见问题

什麼是 Bun?

Bun 是一個 JavaScript 運行時,旨在實現快速且高效的運行。它建立在 JavaScriptCore 引擎之上,並使用了許多優化(使用低階語言 Zig 的優勢)來提高其速度。

如何部署Bun應用程式?

– 建立一個 Bun 應用程式。
– 編寫一個 Dockerfile。
– 將您的 Bun 應用程式推送到 GitHub。
– 開啟 Back4app 帳戶或登入您現有的帳戶。
– 在 Back4app 上建立一個新的「CaaS」應用程式。
– 授予 Back4app 對您想要部署的應用程式的存取權。
– 選擇應用程式並填寫配置詳細資訊。
– 按一下部署。


Leave a reply

Your email address will not be published.