如果您以前没有使用过 Gatsby,请阅读一下为什么它 在所有重要的方面都很快,如果您以前没有使用过 FaunaDB ,您将获得额外的享受。如果您想将静态网站打造为完整的 Jamstack 应用程序,这就是适合您的后端解决方案!
本教程将重点介绍使用 FaunaDB 为 Gatsby 博客提供评论系统的操作。该 应用程序 附带允许用户在您的帖子中发表评论的输入字段,以及一个管理区域,供您批准或删除评论,然后才会在每个帖子中显示。身份验证由 Netlify 的 Identity 小部件提供,所有这些都使用 Netlify 无服务器函数和 Apollo/GraphQL API 结合在一起,将数据推送到 FaunaDB 数据库集合中。

我选择 FaunaDB 作为数据库的原因有很多。首先,它有非常慷慨的免费层!非常适合那些需要后端的小型项目,它对 GraphQL 查询提供原生支持,并具有一些非常强大的索引功能!
… 引用创建者的话:
无论您使用哪个堆栈,或将您的应用程序部署到何处,FaunaDB 都能为您提供通过您熟悉的 API 轻松、低延迟、可靠地访问您的数据的途径
您可以查看完成的评论应用程序 这里。
入门
要开始,请在 https://github.com/PaulieScanlon/fauna-gatsby-comments 克隆仓库
或者
git clone https://github.com/PaulieScanlon/fauna-gatsby-comments.git
然后安装所有依赖项
npm install
也 cd
到 functions/apollo-graphql
并安装 Netlify 函数的依赖项
npm install
这是一个单独的包,有自己的依赖项,您稍后将使用它。
我们还需要安装 Netlify CLI,您稍后也会用到它
npm install netlify-cli -g
现在,让我们添加三个不在仓库中的新文件。
在您项目的根目录下创建 .env
.env.development
和 .env.production
将以下内容添加到 .env
GATSBY_FAUNA_DB =
GATSBY_FAUNA_COLLECTION =
将以下内容添加到 .env.development
GATSBY_FAUNA_DB =
GATSBY_FAUNA_COLLECTION =
GATSBY_SHOW_SIGN_UP = true
GATSBY_ADMIN_ID =
将以下内容添加到 .env.production
GATSBY_FAUNA_DB =
GATSBY_FAUNA_COLLECTION =
GATSBY_SHOW_SIGN_UP = false
GATSBY_ADMIN_ID =
您稍后会回到这里,但如果您想知道
GATSBY_FAUNA_DB
是您数据库的 FaunaDB 密钥GATSBY_FAUNA_COLLECTION
是 FaunaDB 集合名称GATSBY_SHOW_SIGN_UP
用于在网站处于生产环境时隐藏“注册”按钮GATSBY_ADMIN_ID
是由 Netlify Identity 为您生成的用户名
如果您是好奇的人,可以通过运行 gatsby develop
或 yarn develop
来体验应用程序,然后在浏览器中导航到 http://localhost:8000
。
FaunaDB
现在让我们开始吧,但在编写任何操作之前,请访问 https://fauna.com/ 并注册!
数据库和集合
- 通过点击 新建数据库 创建一个新的数据库
- 命名数据库:我将演示数据库命名为
fauna-gatsby-comments
- 通过点击 新建集合 创建一个新的集合
- 命名集合:我将演示集合命名为
demo-blog-comments

服务器密钥
现在,您需要设置一个服务器密钥。转到 安全性
- 通过点击 新建密钥 创建一个新的密钥
- 选择您希望密钥应用于的数据库,例如
fauna-gatsby-comments
- 将 角色 设置为 管理员
- 命名服务器密钥:我将演示密钥命名为
demo-blog-server-key

环境变量第 1 部分
复制服务器密钥并将其添加到 .env.development
、.env.production
和 .env
中的 GATSBY_FAUNA_DB
。
您还需要将集合名称添加到 .env.development
、.env.production
和 .env
中的 GATSBY_FAUNA_COLLECTION
。
将这些值添加到 .env
只是为了让您可以测试您的开发 FaunaDB 操作,您将在下一步中进行。
让我们从创建一个评论开始,回到 boop.js
// boop.js
...
// CREATE COMMENT
createComment: async () => {
const slug = "/posts/some-post"
const name = "some name"
const comment = "some comment"
const results = await client.query(
q.Create(q.Collection(COLLECTION_NAME), {
data: {
isApproved: false,
slug: slug,
date: new Date().toString(),
name: name,
comment: comment,
},
})
)
console.log(JSON.stringify(results, null, 2))
return {
commentId: results.ref.id,
}
},
...
此函数的细分如下:
q
是faunadb.query
的实例Create
是 FaunaDB 方法,用于在集合中创建条目Collection
是数据库中用于存储数据的区域。它将集合名称作为第一个参数,数据对象作为第二个参数。
第二个参数是您需要驱动应用程序评论系统的数据形状。
现在,您将硬编码 slug
、 name
和 comment
,但在最终的应用程序中,这些值将由帖子页面上的输入表单捕获并通过参数传递进来
形状的细分如下:
isApproved
是评论的状态,默认情况下为 false,直到我们在管理页面中批准它slug
是评论被写入的帖子的路径date
是评论被写入的时间戳name
是用户在评论表单中输入的名称comment
是用户在评论表单中输入的评论
当您(或用户)创建评论时,您并不真正关心处理响应,因为就用户而言,他们只会在评论成功或失败时看到成功或错误消息。
在用户发布评论后,它将进入您的管理队列,直到您批准它,但如果您确实想返回一些内容,您可以通过从 createComment
函数中返回一些内容来在 UI 中显示它。
创建评论
如果您已经硬编码了 slug
、 name
和 comment
,现在可以在 CLI 中运行以下代码
node boop createComment
如果一切正常,您应该在终端中看到新评论的日志。
{
"ref": {
"@ref": {
"id": "263413122555970050",
"collection": {
"@ref": {
"id": "demo-blog-comments",
"collection": {
"@ref": {
"id": "collections"
}
}
}
}
}
},
"ts": 1587469179600000,
"data": {
"isApproved": false,
"slug": "/posts/some-post",
"date": "Tue Apr 21 2020 12:39:39 GMT+0100 (British Summer Time)",
"name": "some name",
"comment": "some comment"
}
}
{ commentId: '263413122555970050' }
如果您转到 FaunaDB 中的 集合 ,您应该在集合中看到您的新条目。

您需要在开发过程中创建更多评论,因此请更改 name
和 comment
的硬编码值,然后再次运行以下代码。
node boop createComment
重复此操作几次,以便最终在数据库中存储至少三个新评论,您将在下一步中使用它们。
按 ID 删除评论
现在您已经可以创建评论,您还需要能够删除评论。
通过添加您在上面创建的评论之一的 commentId
,可以将其从数据库中删除。 commentId
是 ref.@ref
对象中的 id
同样,您并不真正关心返回值,但如果您想在 UI 中显示它,可以通过从 deleteCommentById
函数中返回一些内容来做到这一点。
// boop.js
...
// DELETE COMMENT
deleteCommentById: async () => {
const commentId = "263413122555970050";
const results = await client.query(
q.Delete(q.Ref(q.Collection(COLLECTION_NAME), commentId))
);
console.log(JSON.stringify(results, null, 2));
return {
commentId: results.ref.id,
};
},
...
此函数的细分如下
client
是 FaunaDB 客户端实例query
是用于从 FaunaDB 获取数据的函数q
是faunadb.query
的实例Delete
是 FaunaDB 删除方法,用于从集合中删除条目Ref
是用于标识条目的唯一 FaunaDB 引用Collection
是数据库中存储数据的区域
如果您已经硬编码了 commentId
,现在可以在 CLI 中运行以下代码
node boop deleteCommentById
如果您返回 FaunaDB 中的 集合 ,您应该看到该条目不再存在于集合中
索引
接下来,您将在 FaunaDB 中创建一个 **索引**。
**索引** 允许您使用特定术语查询数据库,并定义要返回的特定数据形状。
在使用 GraphQL 和/或 TypeScript 时,这非常强大,因为您可以使用 FaunaDB 索引来返回 *仅* 您需要的 数据,并以可预测的形状。这使得在 GraphQL 和/或 TypeScript 中对数据类型进行响应成为一件轻而易举的事……我曾参与过多个应用程序的开发,这些应用程序只是返回一个巨大的无用值对象,这不可避免地会导致应用程序中的错误。令人沮丧!

- 转到 **索引**,然后点击 **新建索引**
- 为索引命名:我将此索引命名为
get-all-comments
- 将 **源集合** 设置为之前设置的集合的名称
如上所述,当您使用此索引查询数据库时,您可以告诉 FaunaDB 您想要返回的条目哪些部分。
您可以通过添加“值”来实现这一点,但请注意,要完全按照下面显示的那样输入值,因为(在 FaunaDB 免费层级上)您在创建索引后无法修改它们,因此如果出现错误,您将不得不删除索引并重新开始……令人沮丧!
您需要添加的值如下:
ref
data.isApproved
data.slug
data.date
data.name
data.comment
添加完所有值后,您可以点击 **保存**。
获取所有评论
// boop.js
...
// GET ALL COMMENTS
getAllComments: async () => {
const results = await client.query(
q.Paginate(q.Match(q.Index("get-all-comments")))
);
console.log(JSON.stringify(results, null, 2));
return results.data.map(([ref, isApproved, slug, date, name, comment]) => ({
commentId: ref.id,
isApproved,
slug,
date,
name,
comment,
}));
},
...
此函数的细分如下
client
是 FaunaDB 客户端实例query
是用于从 FaunaDB 获取数据的函数q
是faunadb.query
的实例Paginate
对响应进行分页Match
返回匹配的结果Index
是您刚刚创建的 *索引* 的名称
此处返回结果的形状是一个数组,其形状与您在 *索引* “值”中定义的形状相同。
如果您运行以下代码,您应该会看到之前创建的所有评论的列表。
node boop getAllComments
按 slug 获取评论
您将采取与上述类似的方法,但这次创建一个新的 *索引*,允许您以不同的方式查询 FaunaDB。这里的主要区别是,当您 **get-comments-by-slug
** 时,您需要告诉 FaunaDB 关于这个特定的 **术语**,您可以通过在 *术语* 字段中添加 data.slug
来实现。

- 转到 **索引**,然后点击 **新建索引**
- 为索引命名,我将此索引命名为
get-comments-by-slug
- 将 **源集合** 设置为之前设置的集合的名称
- 在术语字段中添加
data.slug
您需要添加的值如下:
ref
data.isApproved
data.slug
data.date
data.name
data.comment
添加完所有值后,您可以点击保存。
// boop.js
...
// GET COMMENT BY SLUG
getCommentsBySlug: async () => {
const slug = "/posts/some-post";
const results = await client.query(
q.Paginate(q.Match(q.Index("get-comments-by-slug"), slug))
);
console.log(JSON.stringify(results, null, 2));
return results.data.map(([ref, isApproved, slug, date, name, comment]) => ({
commentId: ref.id,
isApproved,
slug,
date,
name,
comment,
}));
},
...
此函数的细分如下
client
是 FaunaDB 客户端实例query
是用于从 FaunaDB 获取数据的函数q
是faunadb.query
的实例Paginate
对响应进行分页Match
返回匹配的结果Index
是您刚刚创建的 *索引* 的名称
此处返回结果的形状是一个数组,其形状与您在 *索引* “值”中定义的形状相同,您可以按照上述相同的方式创建此形状,并确保为术语添加一个值。同样,请注意谨慎输入这些值。
如果您运行以下代码,您应该会看到之前创建的所有评论的列表,但针对的是特定的 slug
node boop getCommentsBySlug
按 ID 批准评论
创建评论时,您会手动将 isApproved
值设置为 false。这将阻止评论在您批准之前显示在应用程序中。
现在,您需要创建一个函数来执行此操作,但您需要硬编码一个 commentId
。使用您之前创建的评论之一的 commentId
// boop.js
...
// APPROVE COMMENT BY ID
approveCommentById: async () => {
const commentId = '263413122555970050'
const results = await client.query(
q.Update(q.Ref(q.Collection(COLLECTION_NAME), commentId), {
data: {
isApproved: true,
},
})
);
console.log(JSON.stringify(results, null, 2));
return {
isApproved: results.isApproved,
};
},
...
此函数的细分如下
client
是 FaunaDB 客户端实例query
是用于从 FaunaDB 获取数据的函数q
是faunadb.query
的实例Update
是 FaundaDB 中用于更新条目的方法Ref
是用于标识条目的唯一 FaunaDB 引用Collection
是数据库中存储数据的区域
如果您已经硬编码了 commentId
,现在可以在 CLI 中运行以下代码
node boop approveCommentById
如果您再次运行 getCommentsBySlug
,您现在应该会看到您为其硬编码了 commentId
的条目的 isApproved
状态已更改为 true
。
node boop getCommentsBySlug
这些是管理应用程序中数据的所需的所有操作。
在存储库中,如果您查看 apollo-graphql.js
(位于 functions/apollo-graphql
中),您将看到所有上述操作。如前所述,硬编码的值将被 args
替换,这些值是从应用程序的各个部分传递进来的。
Netlify
假设您已完成 Netlify 注册流程或已拥有 Netlify 帐户,您现在可以将演示应用程序推送到您的 GitHub 帐户。
为此,您需要在本地初始化 Git,添加一个远程仓库,并将演示存储库推送到上游,然后再继续。
您现在应该能够将存储库链接到 Netlify 的 持续部署。
如果您点击 Netlify 仪表板上的“从 Git 创建新站点”按钮,您可以授权访问您的 GitHub 帐户,并选择 gatsby-fauna-comments
存储库以启用 Netlify 的持续部署。您需要至少部署一次,以便我们拥有应用程序的公共 URL。
URL 类似于 https://ecstatic-lewin-b1bd17.netlify.app
,但您可以随意重命名它,并记下 URL,因为您将在稍后提到的 Netlify Identity 步骤中用到它。

环境变量第 2 部分
在之前的步骤中,您将 FaunaDB 数据库密钥和集合名称添加到您的 .env
文件(s) 中。您还需要将它们添加到 Netlify 的 **环境变量** 中。
- 从 Netlify 导航中转到设置
- 点击 **构建和部署**
- 选择 **环境** 或向下滚动,直到看到 **环境变量**
- 点击 **编辑变量**
继续添加以下内容:
GATSBY_SHOW_SIGN_UP = false
GATSBY_FAUNA_DB = you FaunaDB secret key
GATSBY_FAUNA_COLLECTION = you FaunaDB collection name
在这里,您还需要修改 **敏感变量策略**,选择 *无限制部署*
Netlify Identity 小部件
之前提到过,创建评论时,isApproved
值将被设置为 false
,这将阻止评论在您(管理员)批准之前显示在博文上。要成为管理员,您需要创建一个 *身份*。
您可以使用 Netlify Identity 小部件 来实现这一点。
如果您已完成上述持续部署步骤,您可以从 Netlify 导航中转到身份页面。

您目前不会在这里看到任何用户,因此让我们使用应用程序来连接这些点,但在此之前,请确保点击 **启用身份**
在您继续之前,我想指出,您将从现在开始使用 netlify dev
而不是 gatsby develop
或 yarn develop
。这是因为您将在应用程序中使用一些“特殊”的 Netlify 方法,并且使用 netlify dev
启动服务器是启动您将使用的各种流程所必需的。
- 使用
netlify dev
启动应用程序 - 导航到
http://localhost:8888/admin/
- 点击标题中的 **注册** 按钮
您还需要将 Netlify Identity 小部件指向您新部署的应用程序 URL。这是我之前提到的您需要记下的 URL,如果您没有重命名应用程序,它将类似于 https://ecstatic-lewin-b1bd17.netlify.app/
,弹出的窗口中会提示您 **设置站点的 URL**。
您现在可以完成必要的注册步骤。
注册后,您会收到一封电子邮件,要求您确认您的身份,确认后,刷新 Netlify 中的身份页面,您应该会看到自己作为用户。
现在是登录时间了,但在您这样做之前,请在 src/components
中找到 Identity.js
,并暂时取消第 14 行上的 console.log()
的注释。这将把 Netlify Identity 用户对象记录到控制台中。
- 重新启动本地服务器
- 再次使用
netlify dev
启动应用程序 - 点击标题中的 *登录* 按钮
如果一切正常,您应该能够看到 netlifyIdentity.currentUser:
的控制台日志,找到 id
键并复制其值。
将其设置为 .env.production
和 .env.development
中 GATSBY_ADMIN_ID =
的值。
您现在可以安全地删除 Identity.js
中第 14 行上的 console.log()
,或者只是再次将其注释掉。
GATSBY_ADMIN_ID = your Netlify Identity user id
…最后
- 重新启动本地服务器
- 再次使用
netlify dev
启动应用程序
现在您应该能够以“管理员”身份登录……太棒了!
导航到 http://localhost:8888/admin/
并登录。
请注意,您现在将使用 **localhost:8888
** 进行开发,而不是 **localhost:8000
**,后者在 Gatsby 开发中更为常见
在您在部署环境中测试之前,请确保返回 Netlify 的 **环境变量**,并将您的 Netlify Identity 用户 id
添加到环境变量中!
- 从 Netlify 导航中转到设置
- 点击 **构建和部署**
- 选择 **环境** 或向下滚动,直到看到 **环境变量**
- 点击 **编辑变量**
继续添加以下内容:
GATSBY_ADMIN_ID = your Netlify Identity user id
如果你玩一下这个应用程序,并在每个帖子中输入一些评论,然后导航回管理页面,你可以选择批准或删除评论。
当然,只有批准的评论才会显示在任何给定的帖子中,而被删除的评论则会永远消失。
如果你在你的项目中使用了本教程,我很乐意在 @pauliescanlon 听到你的消息。
作者:Paulie Scanlon (@pauliescanlon),前端 React UI 开发人员/UX 工程师:说到底,结构 + 秩序 = 乐趣。
访问 Paulie 的博客:www.paulie.dev
我一直在尝试遵循本教程,从一开始我就遇到了问题。看起来 npm 不是要使用的正确包管理器,npm install 抛出了依赖错误,当我尝试 yarn install 时,它就像魅力一样工作了。
只是把它留在这里,以防有人尝试遵循并卡住了。