1import * as AWS from "alchemy/AWS";
2import { eq, sql } from "drizzle-orm";
3import * as Duration from "effect/Duration";
4import * as Effect from "effect/Effect";
5import * as Schema from "effect/Schema";
6import { HttpServerRequest } from "effect/http/HttpServerRequest";
7import * as HttpServerResponse from "effect/http/HttpServerResponse";
8import { connectDatabase } from "./client.ts";
9import { Todos } from "./schema.ts";
10
11export default class Api extends AWS.Lambda.Function<Api>()(
12 "Api",
13 {
14 main: import.meta.url,
15 runtime: "nodejs22.x",
16 functionUrl: { authType: "AWS_IAM" },
17 timeout: Duration.seconds(30),
18 build: { install: ["pg"] },
19 },
20 Effect.gen(function* () {
21 const db = yield* connectDatabase;
22 return {
23 fetch: Effect.gen(function* () {
24 const request = yield* HttpServerRequest;
25 const url = new URL(request.originalUrl);
26 if (request.method === "GET" && url.pathname === "/health") {
27 const result = yield* db.execute(
28 sql`SELECT current_user AS username`,
29 );
30 return yield* HttpServerResponse.json(result);
31 }
32 if (url.pathname === "/todos" && request.method === "GET") {
33 return yield* HttpServerResponse.json(yield* db.select().from(Todos));
34 }
35 if (url.pathname === "/todos" && request.method === "POST") {
36 const input = yield* request.json.pipe(
37 Effect.flatMap(
38 Schema.decodeUnknownEffect(
39 Schema.Struct({
40 id: Schema.String.check(Schema.isUUID()),
41 text: Schema.NonEmptyString,
42 }),
43 ),
44 ),
45 );
46 return yield* HttpServerResponse.json(
47 yield* db.insert(Todos).values(input).returning(),
48 { status: 201 },
49 );
50 }
51 if (url.pathname.startsWith("/todos/")) {
52 const id = yield* Schema.decodeUnknownEffect(
53 Schema.String.check(Schema.isUUID()),
54 )(url.pathname.slice(7));
55 if (request.method === "PATCH") {
56 const input = yield* request.json.pipe(
57 Effect.flatMap(
58 Schema.decodeUnknownEffect(
59 Schema.Struct({ done: Schema.Boolean }),
60 ),
61 ),
62 );
63 return yield* HttpServerResponse.json(
64 yield* db
65 .update(Todos)
66 .set(input)
67 .where(eq(Todos.id, id))
68 .returning(),
69 );
70 }
71 if (request.method === "DELETE") {
72 yield* db.delete(Todos).where(eq(Todos.id, id));
73 return HttpServerResponse.empty({ status: 204 });
74 }
75 }
76 return HttpServerResponse.text("Not found", { status: 404 });
77 }).pipe(
78 Effect.catchTag("SchemaError", () =>
79 Effect.succeed(
80 HttpServerResponse.text("Invalid request", { status: 400 }),
81 ),
82 ),
83 Effect.orDie,
84 ),
85 };
86 }).pipe(Effect.provide(AWS.DSQL.ConnectHttp)),
87) {}