跳到主要内容
Persistent data npm modules in Deno.

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

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

这篇博文将向你展示如何快速开始在 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 中创建数据库模式

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 仓库,添加远程 origin,并推送仓库。

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

点击 “New Project”(新建项目)并选择 “Import a Prisma Repository”(导入 Prisma 仓库)。

它会要求提供 PostgreSQL 连接字符串,你可以在 .env 文件中找到。将其粘贴在此处。然后点击 “Create Project”(创建项目)。

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

使用 PrismaPrismaClient

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

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

从这里,你可以 seed 你的数据库query 你的数据等等。

查看我们更深入的 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://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 服务器,它将允许我们 query 一些本地数据。我们只需要三个文件就可以完成这个操作

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

我们将从创建它们开始

touch schema.ts resolvers.ts main.ts

让我们逐步完成每个设置。

在此处查看源代码。

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

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

这也是我们描述允许对我们的数据进行的 queries 的地方,使用 GraphQL 中特殊的 Query 类型。我们有两个 queries

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

我们将把所有这些导出到我们的 typeDefs 类型定义变量中

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

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

如果我们想写入数据,这也是我们描述 Mutation 来执行此操作的地方。Mutations 是你如何使用 GraphQL 写入数据的方式。因为我们在这里使用静态数据集,所以我们不会写入任何内容。

resolvers.ts 中填充数据

resolver 负责为每个 query 填充数据。在这里,我们有我们的恐龙列表,而 resolver 将要做的就是:a) 如果用户请求 dinosaurs query,则将整个列表传递给客户端;或者 b) 如果用户请求 dinosaur query,则只传递一个。

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);
    },
  },
};

对于后者,我们将来自客户端的参数传递给一个函数,以将 name 与我们数据集中的 name 匹配。

设置 main.ts

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

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 sandbox,我们可以在其中输入我们的 dinosaurs query

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 上获取帮助。