Drizzle <> Cloudflare Durable Objects SQLite

This guide assumes familiarity with:

Drizzle ORM์€ Cloudflare Durable Objects ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ Cloudflare Workers ํ™˜๊ฒฝ์„ ์™„์ „ํžˆ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ €ํฌ๋Š” SQL ๋ฐฉ์–ธ๊ณผ ๋ฐฉ์–ธ๋ณ„ ๋“œ๋ผ์ด๋ฒ„ ๋ฐ ๊ตฌ๋ฌธ์„ ์ฑ„ํƒํ•˜๊ณ  ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” SQLite์™€ ์œ ์‚ฌํ•œ all, get, values ๋ฐ run ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ ๊ตฌ๋ฌธ์„ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.

Cloudflare Durable Objects ํ”„๋กœ์ ํŠธ๋ฅผ ์„ค์ •ํ•˜๋ ค๋ฉด **๊ณต์‹ ๋ฌธ์„œ**๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

1๋‹จ๊ณ„ - ํŒจํ‚ค์ง€ ์„ค์น˜

npm
yarn
pnpm
bun
npm i drizzle-orm
npm i -D drizzle-kit

2๋‹จ๊ณ„ - ๋“œ๋ผ์ด๋ฒ„ ์ดˆ๊ธฐํ™” ๋ฐ ์ฟผ๋ฆฌ ์‹คํ–‰

Durable Objects ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์œ„ํ•ด wrangler.toml ํŒŒ์ผ์ด ํ•„์š”ํ•˜๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค:

#:schema node_modules/wrangler/config-schema.json
name = "sqlite-durable-objects"
main = "src/index.ts"
compatibility_date = "2024-11-12"
compatibility_flags = [ "nodejs_compat" ]

# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#durable-objects
[[durable_objects.bindings]]
name = "MY_DURABLE_OBJECT"
class_name = "MyDurableObject"

# Durable Object migrations.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#migrations
[[migrations]]
tag = "v1"
new_sqlite_classes = ["MyDurableObject"]

# We need rules so we can import migrations in the next steps
[[rules]]
type = "Text"
globs = ["**/*.sql"]
fallthrough = true

์ฒซ Durable Objects SQLite ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”:

/// <reference types="@cloudflare/workers-types" />
import { drizzle, DrizzleSqliteDODatabase } from 'drizzle-orm/durable-sqlite';
import { DurableObject } from 'cloudflare:workers'
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
import migrations from '../drizzle/migrations';
import { usersTable } from './db/schema';

export class MyDurableObject extends DurableObject {
	storage: DurableObjectStorage;
	db: DrizzleSqliteDODatabase<any>;

	constructor(ctx: DurableObjectState, env: Env) {
		super(ctx, env);
		this.storage = ctx.storage;
		this.db = drizzle(this.storage, { logger: false });

		// ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜๋ฝํ•˜๊ธฐ ์ „์— ๋ชจ๋“  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์™„๋ฃŒ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
		// ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Drizzle ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค `this.db`์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ชจ๋“  ํ•จ์ˆ˜์—์„œ `this.migrate()`๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
		ctx.blockConcurrencyWhile(async () => {
			await this._migrate();
		});
	}

	async insertAndList(user: typeof usersTable.$inferInsert) {
		await this.insert(user);
		return this.select();
	}

	async insert(user: typeof usersTable.$inferInsert) {
		await this.db.insert(usersTable).values(user);
	}

	async select() {
		return this.db.select().from(usersTable);
	}

	async _migrate() {
		migrate(this.db, migrations);
	}
}

export default {
	/**
	 * ์ด๊ฒƒ์€ Cloudflare Worker์˜ ํ‘œ์ค€ fetch ํ•ธ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค
	 *
	 * @param request - ํด๋ผ์ด์–ธํŠธ์—์„œ Worker๋กœ ์ œ์ถœ๋œ ์š”์ฒญ
	 * @param env - wrangler.toml์— ์„ ์–ธ๋œ ๋ฐ”์ธ๋”ฉ์„ ์ฐธ์กฐํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค
	 * @param ctx - Worker์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ
	 * @returns ํด๋ผ์ด์–ธํŠธ๋กœ ๋‹ค์‹œ ์ „์†ก๋  ์‘๋‹ต
	 */
	async fetch(request: Request, env: Env): Promise<Response> {
		const id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName('durable-object');
		const stub = env.MY_DURABLE_OBJECT.get(id);

		// ์˜ต์…˜ A - ์ตœ๊ณ ์˜ ์„ฑ๋Šฅ.
		// DO ๋‚ด์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์•ก์„ธ์Šค๊ฐ€ ๋น ๋ฅด๋ฏ€๋กœ, ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒํ˜ธ ์ž‘์šฉ์„ ๋‹จ์ผ Durable Object ํ˜ธ์ถœ๋กœ ๋ฒˆ๋“ค๋งํ•˜๋Š” ๊ฒƒ์ด ์ตœ๋Œ€ ์„ฑ๋Šฅ์„ ์œ„ํ•ด ์„ ํ˜ธ๋ฉ๋‹ˆ๋‹ค.
		const usersAll = await stub.insertAndList({
			name: 'John',
			age: 30,
			email: 'john@example.com',
		});
		console.log('New user created. Getting all users from the database: ', users);

		// ์˜ต์…˜ B - ๋А๋ฆฌ์ง€๋งŒ ๋•Œ๋•Œ๋กœ ๋””๋ฒ„๊น…์— ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
		// ๋…ธ์ถœ๋œ ๊ฒฝ์šฐ ๊ฐœ๋ณ„ Drizzle ์ฟผ๋ฆฌ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋ชจ๋“  ์ฟผ๋ฆฌ๋Š” Durable Object ์ธ์Šคํ„ด์Šค๋กœ์˜ ์™•๋ณต์ด๋ผ๋Š” ์ ์„ ๋ช…์‹ฌํ•˜์„ธ์š”.
		await stub.insert({
			name: 'John',
			age: 30,
			email: 'john@example.com',
		});
		console.log('New user created!');

		const users = await stub.select();
		console.log('Getting all users from the database: ', users);

		return Response.json(users);
	}
}

๋‹ค์Œ ๋‹จ๊ณ„