Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,6 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Wrangler cache
**/.wrangler/*
50 changes: 50 additions & 0 deletions durable-objects-database-per-user/src/durableDatabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { DurableObject } from 'cloudflare:workers';
import { drizzle } from 'drizzle-orm/durable-sqlite';
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
import { Bindings } from '../bindings';
import migrations from '../drizzle/migrations';
import * as notes from "./db/index";
import * as schema from "./db/schemas";

import { DB } from './db/types';

export class DurableDatabase extends DurableObject {
db: DB;

constructor(ctx: DurableObjectState, env: Bindings) {
super(ctx, env);
// Initialize Drizzle with the Durable Object's storage
this.db = drizzle(ctx.storage, { schema, logger: true });

// Run migrations before accepting any requests
ctx.blockConcurrencyWhile(async () => {
await this._migrate();
});
}

async notesCreate(note: Parameters<typeof notes.create>[1]): ReturnType<typeof notes.create> {
return await notes.create(this.db, note);
}

async notesGet(params: Parameters<typeof notes.get>[1]): ReturnType<typeof notes.get> {
return await notes.get(this.db, params);
}

async notesList(): ReturnType<typeof notes.list> {
return await notes.list(this.db);
}

async notesDel(params: Parameters<typeof notes.get>[1]): ReturnType<typeof notes.del> {
return await notes.del(this.db, params);
}

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

export function getDurableDatabaseStub(env: Bindings, userId: string) {
const doId = env.DurableDatabase.idFromName(userId);
return env.DurableDatabase.get(doId);
}

82 changes: 21 additions & 61 deletions durable-objects-database-per-user/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,47 @@
import { DurableObject } from 'cloudflare:workers';
import { Hono } from 'hono'
import { Hono } from 'hono';
import { Bindings } from '../bindings';
import { drizzle } from 'drizzle-orm/durable-sqlite';
import { migrate } from 'drizzle-orm/durable-sqlite/migrator';
import migrations from '../drizzle/migrations';
import * as schema from "./db/schemas";
import * as notes from "./db/index";
import { DurableDatabase, getDurableDatabaseStub } from './durableDatabase';
export { DurableDatabase } from './durableDatabase';

import { DB } from './db/types';
// Add a type for the variables stored in the Hono context
type Variables = {
stub: DurableObjectStub;
};

const STUB = 'stub';

const app = new Hono<{ Bindings: Bindings }>();
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();

app.get('/', async (c) => {
return c.json({ message: "Hello World!" })
});

export default {
async fetch(request: Request, env: Bindings, ctx: ExecutionContext): Promise<Response> {
return app.fetch(request, env, ctx);
},
};

function getDurableDatabaseStub(env: Bindings, userId: string) {
const doId = env.DurableDatabase.idFromName(userId);
return env.DurableDatabase.get(doId);
}
app.use('/:userId/*', async (c, next) => {
const userId = c.req.param("userId");
const stub = getDurableDatabaseStub(c.env, userId);
c.set(STUB, stub);
await next();
});

// Create a note for a user
app.post('/:userId', async (c) => {
const userId = c.req.param("userId");
const { text } = await c.req.json();
const stub = getDurableDatabaseStub(c.env, userId);
const stub = c.get(STUB) as DurableObjectStub<DurableDatabase>;
const note = await stub.notesCreate({ text });
return c.json({ note })
});

// List all notes for a user
app.get('/:userId', async (c) => {
const userId = c.req.param("userId");
const stub = getDurableDatabaseStub(c.env, userId);
const stub = c.get(STUB) as DurableObjectStub<DurableDatabase>;
const notes = await stub.notesList()
return c.json({ notes })
});

// Get a specific note for a user
app.get('/:userId/:noteId', async (c) => {
const userId = c.req.param("userId");
const noteId = c.req.param("noteId");
const stub = getDurableDatabaseStub(c.env, userId);
const stub = c.get(STUB) as DurableObjectStub<DurableDatabase>;
const note = await stub.notesGet({ id: noteId });
if (!note) {
return c.notFound();
Expand All @@ -57,44 +51,10 @@ app.get('/:userId/:noteId', async (c) => {

// Delete a note for a user
app.delete('/:userId/:noteId', async (c) => {
const userId = c.req.param("userId");
const noteId = c.req.param("noteId");
const stub = getDurableDatabaseStub(c.env, userId);
const stub = c.get(STUB) as DurableObjectStub<DurableDatabase>;
const note = await stub.notesDel({ id: noteId });
return c.json({ note })
});

export class DurableDatabase extends DurableObject {
private db: DB;

constructor(ctx: DurableObjectState, env: Bindings) {
super(ctx, env);
// Initialize Drizzle with the Durable Object's storage
this.db = drizzle(ctx.storage, { schema, logger: true });

// Run migrations before accepting any requests
ctx.blockConcurrencyWhile(async () => {
await this._migrate();
});
}

async notesCreate(note: Parameters<typeof notes.create>[1]): ReturnType<typeof notes.create> {
return await notes.create(this.db, note);
}

async notesGet(params: Parameters<typeof notes.get>[1]): ReturnType<typeof notes.get> {
return await notes.get(this.db, params);
}

async notesList(): ReturnType<typeof notes.list> {
return await notes.list(this.db);
}

async notesDel(params: Parameters<typeof notes.get>[1]): ReturnType<typeof notes.del> {
return await notes.del(this.db, params);
}

private async _migrate() {
await migrate(this.db, migrations);
}
}
export default app;