使用 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 字符串。
Prisma
和 PrismaClient
使用 现在,您将能够将 Prisma
和 PrismaClient
导入您的 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.
成功!
Apollo
Apollo 是一个 GraphQL 服务器,您可以在几分钟内设置它,并与您现有的数据源(或 REST API)一起使用。您可以将任何 GraphQL 客户端连接到它以访问数据,并利用类型检查和高效的获取功能。
让我们启动并运行一个简单的 Apollo 服务器,它将允许我们查询一些本地数据。为此,我们只需要三个文件
schema.ts
用于设置我们的数据模型resolvers.ts
用于设置如何在我们的模式中填充数据字段- 我们的
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
查询,将整个列表传递给客户端,或者如果用户请求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}`);
我们将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上获取帮助。