1---
2title: "Part 2: Mount the HTTP API"
3description: Serve Better Auth routes and an Effect HTTP API from the same Worker.
4sidebar:
5 order: 2
6---
7
8Continue from [Part 1](/better-auth/tutorial/part-1). Keep Better Auth's routes public while building your application's API alongside them.
9
10## Describe a health endpoint
11
12```typescript
13// src/api.ts
14import * as Schema from "effect/Schema";
15import * as HttpApi from "effect/http-api/HttpApi";
16import * as HttpApiEndpoint from "effect/http-api/HttpApiEndpoint";
17import * as HttpApiGroup from "effect/http-api/HttpApiGroup";
18
19export class PublicApi extends HttpApiGroup.make("public").add(
20 HttpApiEndpoint.get("health", "/api/health", {
21 success: Schema.Struct({ ok: Schema.Boolean }),
22 }),
23) {}
24
25export class AppApi extends HttpApi.make("app").add(PublicApi) {}
26```
27
28The schema declares the response shape. No authentication is required for this endpoint.
29
30## Implement the endpoint
31
32```typescript
33// src/http.ts
34import * as Http from "alchemy/Http";
35import * as Effect from "effect/Effect";
36import * as Layer from "effect/Layer";
37import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
38import { AppApi } from "./api.ts";
39
40const PublicLive = HttpApiBuilder.group(AppApi, "public", (handlers) =>
41 handlers.handle("health", () => Effect.succeed({ ok: true })),
42);
43
44export const HttpLive = HttpApiBuilder.layer(AppApi).pipe(
45 Layer.provide(PublicLive),
46 Layer.provide(Http.Platform),
47);
48```
49
50The handler supplies the declared response. Alchemy's HTTP platform layer provides the services needed to build the API on a Worker.
51
52## Build the router
53
54```diff lang="typescript"
55 // src/worker.ts
56+import * as HttpRouter from "effect/http/HttpRouter";
57+import { HttpLive } from "./http.ts";
58
59 Effect.gen(function* () {
60 yield* Auth;
61- return { fetch: Effect.succeed(HttpServerResponse.text("Ready")) };
62+ const fetch = yield* HttpRouter.toHttpEffect(HttpLive);
63+ return { fetch };
64```
65
66Build the router once during Worker construction. Remove the now-unused `HttpServerResponse` import.
67
68## Mount Better Auth
69
70```diff lang="typescript"
71 // src/worker.ts
72+import { HttpServerRequest } from "effect/http/HttpServerRequest";
73
74 Effect.gen(function* () {
75- yield* Auth;
76+ const auth = yield* Auth;
77 const fetch = yield* HttpRouter.toHttpEffect(HttpLive);
78- return { fetch };
79+ return {
80+ fetch: Effect.gen(function* () {
81+ const request = yield* HttpServerRequest;
82+ const pathname = request.url.split("?")[0];
83+ if (pathname === "/api/auth" || pathname.startsWith("/api/auth/")) {
84+ return yield* auth.fetch;
85+ }
86+ return yield* fetch;
87+ }),
88+ };
89```
90
91Better Auth handles its own endpoints and response cookies. Other paths go through the Effect HTTP API.
92
93## Check the public API
94
95```sh
96curl "$DEV_URL/api/health"
97```
98
99```json
100{"ok":true}
101```
102
103## Check an anonymous session
104
105```sh
106curl "$DEV_URL/api/auth/get-session"
107```
108
109```json
110null
111```
112
113An anonymous request has no session. A database or authentication failure is an error, not an anonymous session.
114
115Continue to [Part 3: Build sign-in](/better-auth/tutorial/part-3).