在我使用 Astro 创建我的博客平台后,我列表中的下一件事是让人们可以(匿名)在博客文章上发表评论。
为此,我需要在我的架构中添加一个数据库。
技术选择
我选择PlanetScale 是因为它是无服务器和 MySQL,这是我的两个标准。
为了与我的 PlanetScale 数据库通信,我选择使用Prisma,一个 Node.js 和 TypeScript ORM。
设置棱镜
我首先添加 Prisma 客户端:npm install prisma @prisma/client
.
安装后,我将 Prisma 模式添加到我的代码库中:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
url String
like_count Int @default(0)
Comment Comment[]
}
model Comment {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
text String
author String
postId Int
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
}
</code></span></span>
然后它变得有点棘手。
因为我是在Vercel的Edge网络上部署我的博客平台,平台到数据库的连接不能有持久连接。
经过一些研究,我发现 Prisma 提供了一个名为Prisma Data Platform
.
一旦我在数据平台上创建了一个帐户,我就能够创建一个Data Proxy
,它为我提供了一个连接字符串以在我的应用程序中使用。
这个连接字符串是我需要放入DATABASE_URL
环境变量中的(在 中使用prisma.schema
)。
为了根据我的 Prisma 架构生成 TypeScript 类型,我刚刚运行了,默认情况下,这将在本地项目的文件夹npx prisma generate
中生成类型。node_modules
设置 PlanetScale
将模式从 Prisma 同步到 PlanetScale 就像npx prisma db push
在终端中运行一样简单。
创建评论
为了从我的前端与我的 API 路由进行通信,我选择使用TanStack Query,这是我最喜欢的工具,用于在 React 中处理客户端 API 调用。
为博客文章添加和列出评论的前端代码如下所示(这部分我用 React 编写):
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-declaration-color)">type</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">Comment</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">@prisma/client</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">useQuery</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">@tanstack/react-query</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">useRef</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">useState</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">Fragment</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">react</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">CommentsUpvotes</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">initialComments</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-name-color)">blogUrl</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}:</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">initialComments</span><span style="color:var(--syntax-text-color)">?:</span> <span style="color:var(--syntax-name-color)">Comment</span><span style="color:var(--syntax-text-color)">[];</span>
<span style="color:var(--syntax-text-color)">blogUrl</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">})</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">formRef</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">useRef</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-name-color)">HTMLFormElement</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">null</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-name-color)">formState</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">setFormState</span><span style="color:var(--syntax-text-color)">]</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">useState</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">idle</span><span style="color:var(--syntax-string-color)">'</span> <span style="color:var(--syntax-error-color)">|</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">loading</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">idle</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">upToDateCommentsQuery</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">useQuery</span><span style="color:var(--syntax-text-color)">({</span>
<span style="color:var(--syntax-name-color)">queryKey</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-string-color)">`comments-</span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">blogUrl</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">`</span><span style="color:var(--syntax-text-color)">],</span>
<span style="color:var(--syntax-name-color)">queryFn</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">allCommentsInDb</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">fetch</span><span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-string-color)">`/api/comments/list?blogUrl=</span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">blogUrl</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">`</span>
<span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">allCommentsInDbJson</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">allCommentsInDb</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">json</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">allCommentsInDbJson</span> <span style="color:var(--syntax-declaration-color)">as</span> <span style="color:var(--syntax-name-color)">Comment</span><span style="color:var(--syntax-text-color)">[];</span>
<span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-name-color)">initialData</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">initialComments</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">onSubmit</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">e&