跳至主要内容
Deno 2 终于发布啦 🎉️
了解更多
Persistent data npm modules in Deno.

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

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

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

请查看我们的手册以获取更多关于如何开始使用持久化数据 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 中创建数据库模式

deno run npm:prisma@^4.5 db push

完成后,我们需要为数据代理生成一个 Prisma 客户端

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

设置 Prisma Data Platform

为了使用 Prisma Data Platform,我们必须创建一个 GitHub 仓库并连接它。因此,让我们初始化仓库、创建一个新的 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 是一个流行的基于模式的库,它为 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://127.0.0.1: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 用于设置如何在我们的模式中填充数据字段
  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查询,将整个列表传递给客户端,或者如果用户请求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和来自模式和解析器的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上获取帮助。