如何使用 Deno 和 YugabyteDB 现代化你的 JavaScript API

0 阅读4分钟

如何使用 Deno 和 YugabyteDB 现代化你的 JavaScript API

Brett Hoyer October 23, 2024

原文链接

大多数开发者都熟悉 Node.js,它在 15 多年前通过 Google 的 V8 运行时引擎将 JavaScript 带到了后端。Node 取得了巨大的成功,提供了非阻塞 I/O(non-blocking I/O),允许高并发。为了改进 Node.js 中感知到的设计缺陷,其原始创建者 Ryan Dahl 于 2018 年发布了 Deno,这是一个基于 V8 和 Rust 构建的竞争性运行时。

Deno 提供了许多引人注目的功能,包括原生 TypeScript 支持、内置的 linting 和测试,以及默认的安全措施。新发布的 Deno 2.0 宣称与 Node.js 向后兼容,并提供更高的性能。

在本文中,我将探讨如何使用 Deno 和 YugabyteDB 创建一个简单的 REST API。

开始使用

我已经在我的机器上运行了 Deno 安装程序,并初始化了一个名为 deno_yugabyte 的项目。Deno 的默认项目结构提供了一个 TypeScript 入口点 main.ts。默认的 TypeScript 编译器提供了一个快速开发的环境,无需耗时的配置。然而,这些抽象并不是不透明的,因此你可以通过 deno.json 文件 来配置工具和依赖。

运行 dev 任务将自动编译代码并监视文件更改。让我们运行这个任务,开始编写一个由 YugabyteDB 支持的简单 REST 服务器。

构建 API 服务器

Deno 为各种 Web 框架提供了集成支持。

在这个示例中,我使用 Oak 作为 HTTP 中间件框架,并使用 YugabyteDB Smart Driver for Node.js 进行数据库交互。没错,Deno 2.0 已经添加了从 NPM 导入包的支持,允许开发者在项目中包含熟悉的工具。

以下是我如何使用 smart driver 初始化连接池。

import { default as PG } from "npm:@yugabytedb/pg";
const { Pool } = PG;
if (import.meta.main) {
 const pool = new Pool({
   host: "localhost",
   user: "yugabyte",
   password: "yugabyte",
   database: "yugabyte",
   port: 5433,
 });
}
...

接下来,我使用这个驱动连接到数据库并执行一些语句。

const dbExists = await pool.query(
   "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'superheroes')"
 );

 if (dbExists?.rows?.[0]?.exists === false) {
   await pool.query(`CREATE TABLE IF NOT EXISTS superheroes(
     id SERIAL PRIMARY KEY,
     name varchar(255),
     powers text[]
   )`);
   // Inserting Superman
   await pool.query(
     `INSERT INTO superheroes (name, powers)
      VALUES ($1, $2)`,
     [
       "Superman",
       [
         "Super strength",
         "Flight",
         "Heat vision",
         "Super speed",
         "Invulnerability",
       ],
     ]
   );

   // Inserting Spider-Man
   await pool.query(
     `INSERT INTO superheroes (name, powers)
      VALUES ($1, $2)`,
     [
       "Spider-Man",
       [
         "Wall-crawling",
         "Super strength",
         "Spider-sense",
         "Super agility",
         "Web-shooting",
       ],
     ]
   );

   // Inserting Wonder Woman
   await pool.query(
     `INSERT INTO superheroes (name, powers)
      VALUES ($1, $2)`,
     [
       "Wonder Woman",
       [
         "Super strength",
         "Flight",
         "Invulnerability",
         "Combat mastery",
         "Healing",
       ],
     ]
   );

由于 Deno 的运行时同时接受 TypeScript 和 JavaScript,让我们添加一个 Superhero 接口。这将定义我们与数据库交互时期望的数据模型。我还导入了 QueryResult 类型以供数据库驱动使用。

interface Superhero {
 id: number;
 name: string;
 powers: string[];
}
import type { QueryResult } from "npm:@types/pg/index.d.ts";

现在,让我们结合一组 REST API 来使用我们的数据库。以下是我如何导入 Oak 并为 CRUD 操作创建端点。

const app = new Application();
const rootRouter = new Router();
const apiRouter = new Router({
   prefix: "/api/v1",
 });

 rootRouter.get("/", (context) => {
   context.response.body = "Welcome to the Superheroes API!";
 });

 apiRouter
   .get("/superheroes", async (context) => {
     // Get all superheroes.
     const result: QueryResult<Superhero> = await pool.query(
       "select * from superheroes;"
     );

     context.response.body = result.rows;
   })
   .get("/superheroes/:id", async (context) => {
     // Get one superhero by id.
     const { id } = context.params;
     const result: QueryResult<Superhero> = await pool.query<Superhero>(
       "select * from superheroes WHERE id = $1;",
       [id]
     );
     context.response.body = result.rows[0];
   })
   .post("/superheroes", async (context: Context) => {
     // Create a new superhero.
     const body = await context.request.body();

     if (body.type === "json") {
       const data: Superhero = await body.value;
       const { name, powers } = data;
       const result: QueryResult<Superhero> = await pool.query(
         "insert into superheroes(name, powers) VALUES ($1, $2) RETURNING *",
         [name, powers]
       );
       context.response.body = result.rows[0];
     } else {
       context.response.status = 400;
       context.response.body = { message: "Invalid request body type" };
     }
   })
   .delete("/superheroes/:id", async (context) => {
     // Delete a superhero by id.
     const { id } = context.params;
     const result: QueryResult<Superhero> = await pool.query(
       "DELETE from superheroes WHERE id = $1 RETURNING *",
       [id]
     );
     context.response.body = result.rows[0];
   });

 /**
  * Setup middleware.
  */

 app.use(rootRouter.routes());
 app.use(apiRouter.routes());

 app.use(rootRouter.allowedMethods());
 app.use(apiRouter.allowedMethods());

 /**
  * Start server.
  */

 await app.listen({ port: 8000 });
}

如前所述,Deno 开箱即用地包含了安全措施,并带有用于启用潜在不安全操作的标志。这允许开发者仅启用程序所需的操作。

例如,我修改了 deno.json 中默认的 dev 任务,以包含 I/O、环境和网络访问以及模块导入的权限。

"tasks": {
   "dev": "deno run --allow-read --allow-write --allow-env --allow-net    --allow-import --watch main.ts"
 }

通过使用这些标志运行 Deno 进程,我可以运行服务器并通过 smart driver 与 YugabyteDB 数据库进行交互。对于不需要 Yugabyte smart driver 的高级集群拓扑负载均衡功能的小型项目,Deno 提供了自己的 PostgreSQL 驱动 deno-postgres

结论

Deno 2.0 通过引入与 Node.js 的向后兼容性和对 NPM 包(如 Yugabyte 的 Node.js Smart Driver)的支持,可能会成为 JavaScript 演进的一个转折点。

通过将这些功能添加到其令人印象深刻的性能基准测试、Web 标准 API、原生 TypeScript 支持和其他增强功能中,Deno 很可能成为开发者构建 JavaScript 服务的默认选择。

想了解更多关于使用 YugabyteDB 开发 JavaScript 应用程序的信息吗?请查看我们最近的文章:

Download YugabyteDB for free today to try it out.

image.png