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

利用 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

相关帖子

2024年10月31日 13:00

Moving Baselime from AWS to Cloudflare: simpler architecture, improved performance, over 80% lower cloud costs

Post-acquisition, we migrated Baselime from AWS to the Cloudflare Developer Platform and in the process, we improved query times, simplified data ingestion, and now handle far more events, all while cutting costs. Here’s how we built a modern, high-performing observability platform on Cloudflare’s network. ...