跳到主要内容
Deno 2.4 发布,带来了 deno bundle、字节/文本导入、OTel 稳定版等新功能
了解更多
Persistent data npm modules in Deno.

在 Deno 中使用 npm(Prisma、Mongoose、Apollo 等)持久化数据。

持久化数据对于构建现代 Web 应用程序至关重要。我们需要它来保存用户信息、向客户收费等等。现在,您可以使用您最喜欢的数据存储技术——Prisma、Mongoose、MySQL 等——与 npm 和 Deno 一起使用。

这篇博文将向您展示如何快速开始在 Deno 中使用这些 npm 模块

查看我们的手册,了解更多关于数据持久化 npm 模块的入门指南.

使用 Deno 更安全地持久化数据

供应链攻击是 npm 上已知的安全问题。Node 默认安装并运行 npm 模块,可以访问所有内容,这使得单个恶意依赖项更容易悄无声息地损害数百万最终用户。

数据存储面临供应链攻击的风险甚至更大,因为生产数据是敏感的,对于业务运营至关重要,并且可以通过环境变量访问。

Deno 的选择性权限模型确保您了解您的依赖项需要访问哪些内容。权限系统足够细粒度,允许您授予对环境变量、文件系统甚至 FFI 的访问权限。

Prisma

Prisma 是一个现代 ORM,拥有顶级的开发者体验,一直是我们最受欢迎的模块之一。以下是使用 Prisma 和 Deno 快速入门的指南。

下一节将向您展示如何快速将 Prisma 与 Deno 连接。

在此处查看源代码或关注我们 YouTube 频道上的视频指南

设置应用程序

让我们创建文件夹 deno-prisma 并进入该目录。

mkdir deno-prisma & cd deno-prisma

然后,让我们用 Deno 运行 prisma init

deno run --allow-read --allow-env --allow-write npm:prisma@^4.5 init

这将生成prisma/schema.prisma。让我们用以下内容更新它

generator client {
  provider = "prisma-client-js"
  previewFeatures = ["deno"]
  output = "../generated/client"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Dinosaur {
  id          Int     @id @default(autoincrement())
  name        String  @unique
  description String
}

Prisma 也应该生成一个包含 DATABASE_URL.env 文件。让我们将 DATABASE_URL 分配给一个 PostgreSQL 连接字符串。在这个例子中,我们将使用来自 Supabase 的免费 PostgreSQL 数据库

更新 .env 文件后,让我们在 Prisma 中创建数据库 schema

deno run npm:prisma@^4.5 db push

完成之后,我们需要为 Data Proxy 生成一个 Prisma 客户端

deno run npm:prisma@^4.5 generate --data-proxy

设置 Prisma Data Platform

为了使用 Prisma Data Platform,我们需要创建并连接一个 GitHub 仓库。所以让我们初始化仓库,创建一个新的 GitHub 仓库,添加远程源,并推送仓库。

完成这些后,注册一个免费的 Prisma Data Platform 账户

点击新项目并选择导入 Prisma 仓库

它会要求您提供 PostgreSQL 连接字符串,该字符串在您的 .env 文件中。将其粘贴到此处。然后点击创建项目

您将收到一个新的连接字符串,它以 prisma:// 开头。让我们获取它并将其分配给您的 .env 文件中的 DATABASE_URL,替换您来自 Supabase 的 PostgreSQL 字符串。

使用 PrismaPrismaClient

现在,您将能够将 PrismaPrismaClient 导入到您的 Deno 项目中

import { Prisma, PrismaClient } from "../generated/client/deno/edge.ts";

从这里,您可以为数据库填充数据查询数据等。

查看我们更深入的 Prisma 使用指南了解如何在 Deno Deploy 上部署 Prisma

Mongoose

Mongoose 是一个流行的、基于 schema 的库,用于为 MongoDB 建模数据。这是一个快速入门指南。

在此处查看源代码观看我们 YouTube 上的视频指南

定义一个模型

让我们在 Mongoose 中定义一个模型。在一个新文件 Dinosaur.ts 中,让我们添加

import { model, Schema } from "npm:mongoose@^6.7";

// Define schema.
const dinosaurSchema = new Schema({
  name: { type: String, unique: true },
  description: String,
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now },
});

// Validations
dinosaurSchema.path("name").required(true, "Dinosaur name cannot be blank.");
dinosaurSchema.path("description").required(
  true,
  "Dinosaur description cannot be blank.",
);

// Export model.
export default model("Dinosaur", dinosaurSchema);

创建一个新的 Dinosaur

让我们创建一个新文件 main.ts,它将

  • 连接到 MongoDB
  • 创建一个新的 Dinosaur
  • 检索名为“Deno”的恐龙
  • console.log 检索到的恐龙
import mongoose from "npm:mongoose@^6.7";
import Dinosaur from "./model/Dinosaur.ts";

await mongoose.connect("mongodb://:27017");

// Create a new Dinosaur.
const deno = new Dinosaur({
  name: "Deno",
  description: "The fastest dinosaur ever lived.",
});

// Insert deno.
await deno.save();

// Find Deno by name.
const denoFromMongoDb = await Dinosaur.findOne({ name: "Deno" });
console.log(
  `Finding Deno in MongoDB -- \n  ${denoFromMongoDb.name}: ${denoFromMongoDb.description}`,
);

您的输出应该是

Finding Deno in MongoDB --
  Deno: The fastest dinosaur ever lived.

成功!

查看我们更深入的 Mongoose 使用指南.

Apollo

Apollo 是一个 GraphQL 服务器,您可以在几分钟内设置好并与现有数据源(或 REST API)一起使用。您可以将任何 GraphQL 客户端连接到它以访问数据并利用类型检查和高效获取。

让我们启动并运行一个简单的 Apollo 服务器,它将允许我们查询一些本地数据。为此我们只需要三个文件

  1. schema.ts 用于设置我们的数据模型
  2. resolvers.ts 用于设置我们如何填充 schema 中的数据字段
  3. 我们的 main.ts,服务器将在此启动

我们从创建它们开始

touch schema.ts resolvers.ts main.ts

让我们逐一设置。

在此处查看源代码。

使用 schema.ts 定义我们的数据

我们的 schema.ts 文件描述了我们的数据。在这种情况下,我们的数据是恐龙列表。我们希望用户能够获取每只恐龙的名称和简短描述。在 GraphQL 语言中,这意味着 Dinosaur 是我们的类型,而 namedescription 是我们的字段。我们还可以为每个字段定义数据类型。在这种情况下,两者都是字符串。

这也是我们使用 GraphQL 中的特殊 Query 类型描述我们允许的数据查询的地方。我们有两个查询

  • dinosaurs 用于获取所有恐龙的列表
  • dinosaur 接受恐龙的 name 作为参数,并返回该类型恐龙的信息。

我们将在 typeDefs 类型定义变量中导出所有这些内容

export const typeDefs = `
  type Dinosaur {
    name: String
    description: String
  }

  type Query {
    dinosaurs: [Dinosaur]
        dinosaur(name: String): Dinosaur
  }
`;

如果我们想写入数据,这里也是我们描述如何进行 Mutation 的地方。Mutation 是您使用 GraphQL 写入数据的方式。由于我们在这里使用静态数据集,所以我们不会写入任何内容。

resolvers.ts 中填充数据

解析器负责为每个查询填充数据。这里我们有我们的恐龙列表,解析器所做的只是 a) 如果用户请求 dinosaurs 查询,则将整个列表传递给客户端,或者 b) 如果用户请求 dinosaur 查询,则只传递一个。

const dinosaurs = [
  {
    name: "Aardonyx",
    description: "An early stage in the evolution of sauropods.",
  },
  {
    name: "Abelisaurus",
    description: '"Abel\'s lizard" has been reconstructed from a single skull.',
  },
];

export const resolvers = {
  Query: {
    dinosaurs: () => dinosaurs,
    dinosaur: (_: any, args: any) => {
      return dinosaurs.find((dinosaur) => dinosaur.name === args.name);
    },
  },
};

在后一种情况下,我们将客户端的参数传递给一个函数,以将名称与我们数据集中的名称匹配。

设置 main.ts

在我们的 main.ts 中,我们将导入 ApolloServer 以及 graphql 和来自 schema 的 typeDefs 和我们的解析器

import { ApolloServer } from "npm:@apollo/server@^4.1";
import { startStandaloneServer } from "npm:@apollo/server@^4.1/standalone";
import { graphql } from "npm:graphql@^16.6";
import { typeDefs } from "./schema.ts";
import { resolvers } from "./resolvers.ts";

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 8000 },
});

console.log(`Server running on: ${url}`);

我们将 typeDefsresolvers 传递给 ApolloServer 以启动新服务器。最后,startStandaloneServer 是一个帮助函数,可以快速启动并运行服务器。

运行服务器

现在剩下的就是运行服务器了

deno run --allow-net --allow-read --allow-env main.ts

您应该在终端中看到 Server running on: 127.0.0.1:8000。如果您访问该地址,您将看到 Apollo 沙盒,我们可以在其中输入 dinosaurs 查询

query {
  dinosaurs {
    name
    description
  }
}

这将返回我们的数据集

{
  "data": {
    "dinosaurs": [
      {
        "name": "Aardonyx",
        "description": "An early stage in the evolution of sauropods."
      },
      {
        "name": "Abelisaurus",
        "description": "\"Abel's lizard\" has been reconstructed from a single skull."
      }
    ]
  }
}

或者如果我们只想要一只 dinosaur

query {
  dinosaur(name:"Aardonyx") {
    name
    description
  }
}

它返回

{
  "data": {
    "dinosaur": {
      "name": "Aardonyx",
      "description": "An early stage in the evolution of sauropods."
    }
  }
}

太棒了!

查看我们更深入的 Apollo 使用指南.

下一步是什么?

这篇博文展示了如何开始使用 Prisma、Mongoose 和 Apollo。我们还有其他数据持久化 npm 模块的入门指南,例如 PlanetScaleRedisMySQL2,并将继续添加更多。

遇到困难?在我们的 Discord 上寻求帮助。