1---
2title: "Part 5: Add your application's API"
3description: Add a typed /me endpoint beside Git and use the caller already supplied by your authentication middleware.
4---
5
6import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
7
8Your Git host already identifies callers. Add `/api/v1/me` to return the current
9user. This part introduces an application API as a sibling of the existing Git
10routes; it uses the same Worker, URL, and authentication.
11
12## Define the response and error
13
14Create `src/api.ts`:
15
16```typescript
17// src/api.ts
18import * as Schema from "effect/Schema";
19
20const User = Schema.Struct({ id: Schema.String });
21
22class Unauthorized extends Schema.TaggedError<Unauthorized>()(
23 "Unauthorized",
24 {},
25 { httpApiStatus: 401 },
26) {}
27```
28
29The endpoint returns the caller's ID. Its error schema declares that a missing
30caller is an HTTP `401`.
31
32## Define the endpoint
33
34Add the API imports:
35
36```diff lang="typescript"
37// src/api.ts
38import * as Schema from "effect/Schema";
39+import * as HttpApi from "effect/http-api/HttpApi";
40+import * as HttpApiEndpoint from "effect/http-api/HttpApiEndpoint";
41+import * as HttpApiGroup from "effect/http-api/HttpApiGroup";
42```
43
44Append the endpoint and its group:
45
46```typescript
47// src/api.ts (append)
48const Me = HttpApiEndpoint.get("me", "/api/v1/me", {
49 success: User,
50 error: Unauthorized,
51});
52
53class AppRoutes extends HttpApiGroup.make("app").add(Me) {}
54export class AppApi extends HttpApi.make("app").add(AppRoutes) {}
55```
56
57`AppRoutes` is your application's group. `AppApi` describes that group, including
58the path and response schema of `/api/v1/me`.
59
60## Implement the endpoint
61
62Add these imports:
63
64```diff lang="typescript"
65// src/api.ts
66import * as Schema from "effect/Schema";
67+import * as Effect from "effect/Effect";
68+import * as Layer from "effect/Layer";
69+import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
70+import { Session } from "./session.ts";
71```
72
73Append the implementation:
74
75```typescript
76// src/api.ts (append)
77const MeLive = HttpApiBuilder.group(AppApi, "app", (h) =>
78 h.handle("me", () =>
79 Effect.gen(function* () {
80 const { user } = yield* Session;
81 if (user === null) return yield* new Unauthorized();
82 return user;
83 }),
84 ),
85);
86
87export const AppApiLive = HttpApiBuilder.layer(AppApi).pipe(
88 Layer.provide(MeLive),
89);
90```
91
92The handler reads `Session`, which the middleware from Part 4 supplies.
93`MeLive` implements your group; `AppApiLive` registers its HTTP routes.
94
95## Compose your routes with Git
96
97Import the new route layer in `src/git.ts`:
98
99```diff lang="typescript"
100// src/git.ts
101import { Authentication } from "./middleware.ts";
102+import { AppApiLive } from "./api.ts";
103```
104
105Merge it beside Git before applying the middleware:
106
107```diff lang="typescript"
108// src/git.ts
109-const PublicRoutes = Git.ApiLive.pipe(
110+const PublicRoutes = Layer.mergeAll(AppApiLive, Git.ApiLive).pipe(
111 Layer.provide(Authentication.layer),
112);
113```
114
115Both sets of routes now run with the same access policy and request session.
116Your application's API definition and handlers stay in `src/api.ts`.
117
118## Deploy the application endpoint
119
120<Tabs syncKey="pkgManager">
121 <TabItem label="bun" icon="bun">
122 <Code code={`bun alchemy deploy`} lang="sh" />
123 </TabItem>
124 <TabItem label="npm" icon="npm">
125 <Code code={`npx alchemy deploy`} lang="sh" />
126 </TabItem>
127 <TabItem label="pnpm" icon="pnpm">
128 <Code code={`pnpm alchemy deploy`} lang="sh" />
129 </TabItem>
130 <TabItem label="yarn" icon="seti:yarn">
131 <Code code={`yarn alchemy deploy`} lang="sh" />
132 </TabItem>
133</Tabs>
134
135No new storage resource is needed for this change.
136
137## Call your endpoint
138
139Use the session cookie from Part 4:
140
141```sh
142curl --fail-with-body -b dana.cookies "$HOST/api/v1/me"
143```
144
145Expect `{"id":"..."}` with the same ID as `$OWNER`. The API key also identifies
146Dana:
147
148```sh
149curl --fail-with-body -u "x:$KEY" "$HOST/api/v1/me"
150```
151
152Without either credential, `/me` returns `401`:
153
154```sh
155curl -i "$HOST/api/v1/me"
156```
157
158Verify that Git still works on the same host:
159
160```sh
161git -c credential.helper= -C work fetch origin
162```
163
164Enter Dana's API key when prompted. Continue to
165[Part 6: Protect a branch](/git/tutorial/part-6).