1import * as AWS from "alchemy/AWS";
2import * as Lambda from "alchemy/AWS/Lambda";
3import * as Duration from "effect/Duration";
4import * as Effect from "effect/Effect";
5import { HttpServerRequest } from "effect/http/HttpServerRequest";
6import * as HttpServerResponse from "effect/http/HttpServerResponse";
7import path from "pathe";
8import { AuroraDataApi } from "../../../src/AuroraDataApi.ts";
9import { BetterAuth } from "../../../src/index.ts";
10
11const main = path.resolve(import.meta.dirname, "aurora-handler.ts");
12
13/**
14 * Aurora Serverless v2 with the Data API (enabled by default). The cluster
15 * needs a VPC, but the Lambda does NOT join it — all access is HTTPS via
16 * the Data API bindings.
17 */
18export const Db = Effect.gen(function* () {
19 const network = yield* AWS.EC2.Network("AuroraAuthNetwork", {
20 cidrBlock: "10.42.0.0/16",
21 availabilityZones: 2,
22 });
23 const securityGroup = yield* AWS.EC2.SecurityGroup(
24 "AuroraAuthDbSecurityGroup",
25 {
26 vpcId: network.vpcId,
27 description: "Better Auth Aurora Data API test cluster",
28 },
29 );
30 return yield* AWS.RDS.Aurora("AuroraAuthDb", {
31 subnetIds: network.privateSubnetIds,
32 securityGroupIds: [securityGroup.groupId],
33 });
34});
35
36// The layer takes the Aurora composite directly — cluster + secret +
37// writer dependency all wired from one value.
38const AuthDatabase = AuroraDataApi(Db, { database: "postgres" });
39
40export class AuroraAuthFunction extends Lambda.Function<Lambda.Function>()(
41 "BetterAuthAuroraFunction",
42) {}
43
44export default AuroraAuthFunction.make(
45 {
46 main,
47 url: true,
48 memorySize: 512,
49 timeout: Duration.seconds(30),
50 },
51 Effect.gen(function* () {
52 const auth = yield* BetterAuth({
53 basePath: "/auth",
54 emailAndPassword: { enabled: true },
55 });
56
57 return {
58 fetch: Effect.gen(function* () {
59 const request = yield* HttpServerRequest;
60 const pathname = new URL(request.url, "http://lambda").pathname;
61 if (pathname.startsWith("/auth")) {
62 return yield* auth.fetch;
63 }
64 if (pathname.startsWith("/me")) {
65 const session = yield* auth
66 .getSession()
67 .pipe(
68 Effect.catchTag("BetterAuthApiError", () => Effect.succeed(null)),
69 );
70 return yield* HttpServerResponse.json({
71 email: session?.user.email ?? null,
72 });
73 }
74 return HttpServerResponse.text("ok");
75 }),
76 };
77 }).pipe(Effect.provide(AuthDatabase)),
78);