1import * as Cloudflare from "alchemy/Cloudflare";
2import * as Drizzle from "alchemy/Drizzle/Postgres";
3import { eq } from "drizzle-orm";
4import { Layer } from "effect";
5import * as Effect from "effect/Effect";
6import * as HttpServerRequest from "effect/http/HttpServerRequest";
7import * as HttpServerResponse from "effect/http/HttpServerResponse";
8import { Hyperdrive } from "./Db.ts";
9import { relations, Users } from "./schema.ts";
10
11export default class Api extends Cloudflare.Worker<Api>()(
12 "Api",
13 {
14 main: import.meta.url,
15 },
16 Effect.gen(function* () {
17 const conn = yield* Cloudflare.Hyperdrive.Connect(Hyperdrive);
18 const db = yield* Drizzle.Postgres(conn.connectionString, {
19 relations,
20 });
21
22 return {
23 fetch: Effect.gen(function* () {
24 const request = yield* HttpServerRequest.HttpServerRequest;
25 switch (request.method) {
26 case "GET": {
27 if (request.url === "/") {
28 const users = yield* db.select().from(Users);
29 return yield* HttpServerResponse.json({ users });
30 }
31 const id = Number(request.url.split("/").pop());
32 if (Number.isNaN(id)) {
33 return yield* HttpServerResponse.json(
34 { error: "Invalid user ID" },
35 { status: 400 },
36 );
37 }
38 const user = yield* db.query.Users.findFirst({
39 where: { id },
40 with: { posts: true },
41 });
42 return yield* HttpServerResponse.json({ user });
43 }
44 case "POST": {
45 const user = yield* db
46 .insert(Users)
47 .values({
48 name: crypto.randomUUID(),
49 email: crypto.randomUUID(),
50 })
51 .returning();
52 return yield* HttpServerResponse.json({ user });
53 }
54 case "DELETE": {
55 const id = Number(request.url.split("/").pop());
56 if (Number.isNaN(id)) {
57 return yield* HttpServerResponse.json(
58 { error: "Invalid user ID" },
59 { status: 400 },
60 );
61 }
62 const [user] = yield* db
63 .delete(Users)
64 .where(eq(Users.id, id))
65 .returning();
66 return yield* HttpServerResponse.json({ user });
67 }
68 default: {
69 return yield* HttpServerResponse.json(
70 { error: "Method not allowed" },
71 { status: 405 },
72 );
73 }
74 }
75 }).pipe(
76 Effect.catch((cause: any) => {
77 const peel = (e: any): any => (e?.cause ? peel(e.cause) : e);
78 const root = peel(cause);
79 return HttpServerResponse.json(
80 {
81 ok: false,
82 error: String(cause),
83 rootError: root?.message ?? String(root),
84 rootCode: root?.code,
85 },
86 { status: 500 },
87 );
88 }),
89 ),
90 };
91 }).pipe(Effect.provide(Layer.mergeAll(Cloudflare.Hyperdrive.ConnectBinding))),
92) {}