Part 2: Mount the HTTP API
Continue from Part 1. Keep Better Auth’s routes public while building your application’s API alongside them.
Describe a health endpoint
Section titled “Describe a health endpoint”import * as Schema from "effect/Schema";import * as HttpApi from "effect/unstable/httpapi/HttpApi";import * as HttpApiEndpoint from "effect/unstable/httpapi/HttpApiEndpoint";import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup";
export class PublicApi extends HttpApiGroup.make("public").add( HttpApiEndpoint.get("health", "/api/health", { success: Schema.Struct({ ok: Schema.Boolean }), }),) {}
export class AppApi extends HttpApi.make("app").add(PublicApi) {}The schema declares the response shape. No authentication is required for this endpoint.
Implement the endpoint
Section titled “Implement the endpoint”import * as Http from "alchemy/Http";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";import { AppApi } from "./api.ts";
const PublicLive = HttpApiBuilder.group(AppApi, "public", (handlers) => handlers.handle("health", () => Effect.succeed({ ok: true })),);
export const HttpLive = HttpApiBuilder.layer(AppApi).pipe( Layer.provide(PublicLive), Layer.provide(Http.Platform),);The handler supplies the declared response. Alchemy’s HTTP platform layer provides the services needed to build the API on a Worker.
Build the router
Section titled “Build the router”import * as HttpRouter from "effect/unstable/http/HttpRouter";import { HttpLive } from "./http.ts";
Effect.gen(function* () { yield* Auth; return { fetch: Effect.succeed(HttpServerResponse.text("Ready")) }; const fetch = yield* HttpRouter.toHttpEffect(HttpLive); return { fetch };Build the router once during Worker construction. Remove the now-unused HttpServerResponse import.
Mount Better Auth
Section titled “Mount Better Auth”import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest";
Effect.gen(function* () { yield* Auth; const auth = yield* Auth; const fetch = yield* HttpRouter.toHttpEffect(HttpLive); return { fetch }; return { fetch: Effect.gen(function* () { const request = yield* HttpServerRequest; const pathname = request.url.split("?")[0]; if (pathname === "/api/auth" || pathname.startsWith("/api/auth/")) { return yield* auth.fetch; } return yield* fetch; }), };Better Auth handles its own endpoints and response cookies. Other paths go through the Effect HTTP API.
Check the public API
Section titled “Check the public API”curl "$DEV_URL/api/health"{"ok":true}Check an anonymous session
Section titled “Check an anonymous session”curl "$DEV_URL/api/auth/get-session"nullAn anonymous request has no session. A database or authentication failure is an error, not an anonymous session.
Continue to Part 3: Build sign-in.