前面把认证表结构和 token 设计拆清楚之后,下一步就该把数据库真正跑起来。
这里先不要急着上线上 D1,先把本地 D1 建出来更适合新手。原因如下:
本地 D1 在开发阶段非常方便,改完 schema、跑完初始化、再发一个本地请求,能立刻看到结果
Cloudflare D1 底层是 SQLite 风格数据库。平时说「本地 D1」,本质上不是在本机起一个全新的数据库产品,而是通过 Wrangler 在本地开发环境里,给 Worker 提供一个 D1 绑定和对应的本地数据库文件。
所以这里有两层概念:
你写代码时,用的是 env.DB 这类绑定名。你本地执行 SQL、初始化表结构时,操作的是 Wrangler 维护的那份本地数据库。
把这两层概念想清楚,后面就不会把「D1 绑定名」「数据库名」「数据库 id」「本地文件」混在一起。
如果项目已经接了 Cloudflare Worker,通常已经有 wrangler.jsonc 或 wrangler.toml。本地 D1 的入口也在这里。
最常见的是在配置里写上 D1 绑定:
1{2"d1_databases": [3{4"binding": "DB",5"database_name": "ai-agent-local-auth",6"database_id": "91e63d07-bee3-4c00-9349-cfde384ba6eb"7}8]9}
这里 3 个字段别看混:
binding:代码里拿来访问数据库的名字,例如 env.DBdatabase_name:D1 这份数据库的别名,可读性更好database_id:Cloudflare 侧识别这份数据库的唯一标识本地开发时,真正最常用的是 binding,因为代码里会一直用它
如果远程 D1 还没建,通常会先创建一份 D1 数据库,再把 database_id 回填到 wrangler 配置里。
命令一般是:
1npx wrangler d1 create ai-agent-local-auth
执行后,Wrangler 会返回一段结果,里面会带数据库名和 database_id。
拿到之后,把 database_id 填回 wrangler.jsonc。
本地 D1 建好之后,接下来就该把表结构灌进去。
最常见的做法,是先准备一份初始化 SQL,比如:
01CREATE TABLE users (02id TEXT PRIMARY KEY,03display_name TEXT,04created_at_ms INTEGER NOT NULL05);0607CREATE TABLE auth_sessions (08id TEXT PRIMARY KEY,09user_id TEXT NOT NULL,10created_at_ms INTEGER NOT NULL11);
然后通过 Wrangler 把这份 SQL 执行到本地 D1:
1npx wrangler d1 execute ai-agent-local-auth --local --file=./sql/init.sql
execute:执行 SQLai-agent-local-auth:目标数据库名--local:明确这次打到本地数据库,不是远程--file:从文件里读 SQL如果只是想临时跑一条 SQL,也可以直接传命令:
1npx wrangler d1 execute ai-agent-local-auth --local --command="SELECT name FROM sqlite_master WHERE type='table'"
这样可以快速确认本地表是不是已经建起来了。
新手刚开始容易图省事,把建表 SQL 散在代码里到处跑。后面表一多,很快就乱。
单独放 SQL 文件有几个很直接的好处:
所以本地 D1 这一步,最好顺手就把目录约定好。例如:
1apps/api/2migrations/30001_init.sql40002_auth_sessions.sql
哪怕现在还没正式上 migration,先把 SQL 明确下来,后面会省很多事。
表建完之后,接下来就是在本地服务里真正连起来。
如果你本地是通过 Wrangler 跑 Worker,D1 绑定会随着本地 Worker 一起注入。你的业务代码里只需要正常使用 env.DB。
例如:
01import { Hono } from 'hono'0203// 注意,这里只是案例代码,实际项目中可能需要更复杂的绑定类型,这里只是为了示例04interface Env {05DB: D1Database06}0708const app = new Hono<{ Bindings: Env }>()0910app.get('/health', async (c) => {11const result = await c.env.DB.prepare('SELECT 1 as ok').first<{ ok: number }>()12return c.json(result)13})1415export default app
这段代码跑起来之后,只要本地 Worker 是通过 Wrangler 启动的,这个 DB 绑定就会指向本地 D1。
所以开发阶段真正的连法不是你手动 new 一个数据库连接,而是:
env 里拿到 D1配置里写的是 binding: "DB",代码里却去拿 env.DATABASE,这种最常见。
跑命令时少了 --local,结果以为自己在调本地,实际打的是远程库。
你改了 xxx.sql,但没有重新执行,或者本地数据库文件还保留着旧状态,这时最容易以为是代码问题
代码里用的是绑定名,Wrangler 命令里用的是数据库名,这两个不是一个东西。