订阅以接收新文章的通知:

利用 Cloudflare D1,将静态网站变为动态

2022-11-16

4 分钟阅读时间
这篇博文也有 EnglishFrançaisDeutsch日本語PortuguêsEspañol繁體中文版本。

简介

Making static sites dynamic with Cloudflare D1

有许多方法可以存储您应用程序中的数据。例如,在 Cloudflare Workers 应用程序中,我们有用于键值存储的 Workers KV 和可以实时协调存储而不影响一致性的 Durable Objects。您还可以插入 Cloudflare 生态系统之外的其他工具,例如 NoSQL 和图形数据库。

但有时,您想使用 SQL。索引帮助我们快速检索数据。连接可用于描述不同表之间的复杂关系。SQL 使用声明式语言描述如何验证、创建和永久查询我们应用程序的数据。

今天发布了 D1 的公开 α 测试。我想借机分享我使用 D1 构建应用程序的经验:特别是如何入门,以及我为什么对 D1 成为 Cloudflare 上构建应用程序的许多工具之一感到兴奋。

D1 的卓越之处在于,它是向应用程序即时添加值,既不需要新的工具,也不需要离开 Cloudflare 生态系统。使用 Wrangler,我们可以对 Workers 应用程序进行本地开发;而在 Wrangler 中添加 D1 后,我们现在还可以在本地开发适当的有状态的应用程序。然后,在部署应用程序的时候,Wrangler 让我们可以访问并执行对 D1 数据库的命令,以及您的 API 本身。

我们正在构建的产品

在这篇博客文章中,我将向您讲述如何使用 D1 向静态博客网站添加评论。为此,我们将构建一个新的 D1 数据库,并建立一个允许创建和检索评论的简单 JSON API。

我之前提到过,将 D1 与应用程序本身分开(与静态网站分开的 API 和数据库),我们能够将网站的静态和动态部分相互剥离。这也简化了应用程序的部署:我们将把前端部署到 Cloudflare Pages,把 D1 支持的 API 部署到 Cloudflare Workers。

构建新的应用程序

首先,我们将在 Workers 中添加一个基本 API。创建一个新目录,并在其中创建一个新的 Wrangler 项目。

在这个示例中,我们将使用 Hono(一个 Express.js 框架)来快速构建 API。要在项目中使用 Hono,需使用 NPM 安装 Hono:

$ mkdir d1-example && d1-example
$ wrangler init

然后,我们将在 src/index.ts 将新的 Hono 应用程序初始化,并定义几个端点:GET /API/posts/:slug/comments 和 POST /get/api/:slug/comments。

$ npm install hono

现在我们将创建一个 D1 数据库。Wrangler 2 支持 wrangler d1 子命令,可以直接从命令行创建和查询 D1 数据库。例如,我们可以使用单个命令创建一个新的数据库。

import { Hono } from 'hono'
import { cors } from 'hono/cors'

const app = new Hono()

app.get('/api/posts/:slug/comments', async c => {
  // do something
})

app.post('/api/posts/:slug/comments', async c => {
  // do something
})

export default app

利用我们创建的数据库,我们可以提取数据库名称 ID,并将其与 wrangler.toml(Wrangler 的配置文件)内的绑定关联起来。绑定让我们可以在代码中使用一个简单的变量名来访问各种 Cloudflare 资源,例如 D1 数据库、KV 命名空间和 R2 存储桶。下面,我们将创建绑定 DB,并用它来表示我们的新数据库:

$ wrangler d1 create d1-example

请注意,这个指令(即 [[d1_databases]] 字段)目前需要使用 Wrangler 的 beta 版本。您可以使用命令 npm install -D wrangler/beta 为您的项目安装该版本。

[[ d1_databases ]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "d1-example"
database_id = "4e1c28a9-90e4-41da-8b4b-6cf36e5abb29"

在我们的 wrangler.toml 中配置数据库后,我们便可从命令行和 Workers 函数中与之互动。

首先,您可以使用 wrangler d1 execute 直接发布 SQL 命令。

您也可以传递一个 SQL 文件——非常适合在单个命令中进行初始数据种子设定。创建 src/schema.sql,为我们的项目创建一个新的 comments 表。

$ wrangler d1 execute d1-example --command "SELECT name FROM sqlite_schema WHERE type ='table'"
Executing on d1-example:
┌─────────────────┐
│ name │
├─────────────────┤
│ sqlite_sequence │
└─────────────────┘

创建文件后,通过向 D1 数据库传递标志 --file,对其执行模式文件。

drop table if exists comments;
create table comments (
  id integer primary key autoincrement,
  author text not null,
  body text not null,
  post_slug text not null
);
create index idx_comments_post_id on comments (post_slug);

-- Optionally, uncomment the below query to create data

-- insert into comments (author, body, post_slug)
-- values ("Kristian", "Great post!", "hello-world");

我们只用了几个命令就创建了一个 SQL 数据库,并种入初始数据。现在我们可以给 Workers 函数添加一个路由,从该数据库获取数据。根据我们的 wrangler.toml 配置,现可通过 DB 绑定访问 D1 数据库。在我们的代码中,我们可以使用绑定来编写并执行 SQL 语句,例如,检索评论:

$ wrangler d1 execute d1-example --file src/schema.sql

在这个函数中,我们接受了一个 slug URL 查询参数,并设置了一个新的 SQL 语句——我们选择了有一个与查询参数匹配的 post_slug 值的所有评论。然后我们可以把它作为一个简单的 JSON 响应返回。

app.get('/api/posts/:slug/comments', async c => {
  const { slug } = c.req.param()
  const { results } = await c.env.DB.prepare(`
    select * from comments where post_slug = ?
  `).bind(slug).all()
  return c.json(results)
})

目前,我们已经构建了对数据的只读访问权限。当然,向 SQL “插入”值也是可能实现的。那么,我们来定义另一个函数,即通过向端点发送 POST 请求来创建新评论:

在这个示例中,我们构建了一个评论 API 来支持博客。如需查看这个 D1 驱动的评论 API 的源代码,请访问 cloudflare/templates/worker-d1-api

app.post('/API/posts/:slug/comments', async c => {
  const { slug } = c.req.param()
  const { author, body } = await c.req.json<Comment>()

  if (!author) return c.text("Missing author value for new comment")
  if (!body) return c.text("Missing body value for new comment")

  const { success } = await c.env.DB.prepare(`
    insert into comments (author, body, post_slug) values (?, ?, ?)
  `).bind(author, body, slug).run()

  if (success) {
    c.status(201)
    return c.text("Created")
  } else {
    c.status(500)
    return c.text("Something went wrong")
  }
})

总结

D1 最令人振奋的优势之一是,有机会利用动态关系型数据增强现有应用程序或网站。作为曾经的 Ruby on Rails 开发人员,面对 JavaScript 和无服务器开发工具的世界,我最怀念该框架的一点是,能够快速启动完整的数据驱动的应用程序,而不需要成为管理数据库基础设施的专家。利用 D1 及其基于 SQL 的数据的简单入口,我们可以构建真正的数据驱动的应用程序,而不会影响性能或开发人员的体验。

这一转变源于过去几年中使用 Hugo 或 Gatsby 等工具的静态网站的出现。使用 Hugo 等静态网站生成器构建的博客可提供不可思议的性能,只需几秒钟即可构建小型资产。

但是,如果把 WordPress 等工具换成静态网站生成器,您就失去了向您的网站添加动态信息的机会。许多开发人员通过增加构建流程的复杂性来解决这个问题:在构建过程中,获取和检索数据并使用这些数据生成页面。

增加构建流程的复杂性试图解决应用程序缺乏动态性的问题,但它仍然不是真正的动态应用程序。这样的应用程序并不能在创建时检索和显示新数据,而是在数据发生变化时重新构建和部署,因此看起来像实时动态呈现数据。您的应用程序可以保持静态,而动态数据的呈现将在地理上靠近网站的用户,可通过富有表现力的可查询 API 访问。

我们保护整个企业网络,帮助客户高效构建互联网规模的应用程序,加速任何网站或互联网应用程序抵御 DDoS 攻击,防止黑客入侵,并能协助您实现 Zero Trust 的过程

从任何设备访问 1.1.1.1,以开始使用我们的免费应用程序,帮助您更快、更安全地访问互联网。要进一步了解我们帮助构建更美好互联网的使命,请从这里开始。如果您正在寻找新的职业方向,请查看我们的空缺职位
Developer Week开发人员ServerlessStorageCloudflare WorkersD1Developer Platform

在 X 上关注

Kristian Freeman|@kristianf_
Cloudflare|@cloudflare

相关帖子