使用 Gatsby 和 FaunaDB 创建自己的评论系统

❥ 赞助商

如果您以前没有使用过 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 developyarn 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.developmentGATSBY_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