1import * as Cloudflare from "alchemy/Cloudflare";
2import * as SQL from "alchemy/SQL/D1";
3import * as Effect from "effect/Effect";
4import * as HttpServerRequest from "effect/http/HttpServerRequest";
5import * as HttpServerResponse from "effect/http/HttpServerResponse";
6import { Database } from "./database.ts";
7
8export interface User {
9 readonly id: number;
10 readonly email: string;
11 readonly name: string;
12 readonly created_at: number;
13}
14
15/**
16 * A Worker querying D1 through `@effect/sql-d1` — tagged-template SQL over
17 * the native binding. Interpolated values are parameterized, every query is
18 * an Effect, and failures surface as typed `SqlError`s.
19 */
20export default class Api extends Cloudflare.Worker<Api>()(
21 "Api",
22 {
23 main: import.meta.url,
24 },
25 Effect.gen(function* () {
26 const d1 = yield* Cloudflare.D1.QueryDatabase(Database);
27 const sql = yield* SQL.D1(d1);
28
29 return {
30 fetch: Effect.gen(function* () {
31 const request = yield* HttpServerRequest.HttpServerRequest;
32 switch (request.method) {
33 case "GET": {
34 if (request.url === "/") {
35 const users = yield* sql<User>`
36 SELECT * FROM users ORDER BY id
37 `;
38 return yield* HttpServerResponse.json({ users });
39 }
40 const id = Number(request.url.split("/").pop());
41 if (Number.isNaN(id)) {
42 return yield* HttpServerResponse.json(
43 { error: "Invalid user ID" },
44 { status: 400 },
45 );
46 }
47 const [user] = yield* sql<User>`
48 SELECT * FROM users WHERE id = ${id}
49 `;
50 return yield* HttpServerResponse.json({ user: user ?? null });
51 }
52 case "POST": {
53 const [user] = yield* sql<User>`
54 INSERT INTO users (name, email)
55 VALUES (${crypto.randomUUID()}, ${crypto.randomUUID()})
56 RETURNING *
57 `;
58 return yield* HttpServerResponse.json({ user });
59 }
60 case "DELETE": {
61 const id = Number(request.url.split("/").pop());
62 if (Number.isNaN(id)) {
63 return yield* HttpServerResponse.json(
64 { error: "Invalid user ID" },
65 { status: 400 },
66 );
67 }
68 const [user] = yield* sql<User>`
69 DELETE FROM users WHERE id = ${id} RETURNING *
70 `;
71 return yield* HttpServerResponse.json({ user: user ?? null });
72 }
73 default: {
74 return yield* HttpServerResponse.json(
75 { error: "Method not allowed" },
76 { status: 405 },
77 );
78 }
79 }
80 }).pipe(
81 Effect.catchCause((cause) =>
82 HttpServerResponse.json({ error: String(cause) }, { status: 500 }),
83 ),
84 ),
85 };
86 }).pipe(Effect.provide(Cloudflare.D1.QueryDatabaseBinding)),
87) {}