在 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 字符串。
Prisma
和 PrismaClient
使用 现在,您将能够将 Prisma
和 PrismaClient
导入到您的 Deno 项目中
import { Prisma, PrismaClient } from "../generated/client/deno/edge.ts";
查看我们更深入的 Prisma 使用指南或了解如何在 Deno Deploy 上部署 Prisma。
Mongoose
Mongoose 是一个流行的、基于 schema 的库,用于为 MongoDB 建模数据。这是一个快速入门指南。
定义一个模型
让我们在 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.
成功!
Apollo
Apollo 是一个 GraphQL 服务器,您可以在几分钟内设置好并与现有数据源(或 REST API)一起使用。您可以将任何 GraphQL 客户端连接到它以访问数据并利用类型检查和高效获取。
让我们启动并运行一个简单的 Apollo 服务器,它将允许我们查询一些本地数据。为此我们只需要三个文件
schema.ts
用于设置我们的数据模型resolvers.ts
用于设置我们如何填充 schema 中的数据字段- 我们的
main.ts
,服务器将在此启动
我们从创建它们开始
touch schema.ts resolvers.ts main.ts
让我们逐一设置。
schema.ts
定义我们的数据
使用 我们的 schema.ts
文件描述了我们的数据。在这种情况下,我们的数据是恐龙列表。我们希望用户能够获取每只恐龙的名称和简短描述。在 GraphQL 语言中,这意味着 Dinosaur
是我们的类型,而 name
和 description
是我们的字段。我们还可以为每个字段定义数据类型。在这种情况下,两者都是字符串。
这也是我们使用 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}`);
我们将 typeDefs
和 resolvers
传递给 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."
}
}
}
太棒了!
下一步是什么?
这篇博文展示了如何开始使用 Prisma、Mongoose 和 Apollo。我们还有其他数据持久化 npm 模块的入门指南,例如 PlanetScale、Redis 和 MySQL2,并将继续添加更多。
遇到困难?在我们的 Discord 上寻求帮助。