1import { Effect, Layer } from "effect"
2import { HttpApiBuilder, HttpApiError } from "effect/http-api"
3import { Api } from "../../api/Api.ts"
4import { CurrentUser } from "../../api/Authorization.ts"
5import { AuthorizationLayer } from "../Authorization.ts"
6import { Users } from "../Users.ts"
7
8// The handlers without their dependencies provided, so tests can supply an
9// alternative `Users` implementation.
10export const UsersApiHandlersNoDeps = HttpApiBuilder.group(
11 Api,
12 "users",
13 Effect.fn(function*(handlers) {
14 const users = yield* Users
15
16 return handlers.handleAll({
17 list: ({ query }) =>
18 users.list(query.search).pipe(
19 // The list endpoint expects no errors, so we convert any potential
20 // errors into a 500 Internal Server Error.
21 Effect.orDie
22 ),
23 search: Effect.fn(function*({ payload }) {
24 if (payload.search === "bad-request") {
25 // You can use the built in error types like any other
26 // Schema.TaggedError
27 return yield* new HttpApiError.RequestTimeout()
28 }
29 return yield* users.list(payload.search).pipe(
30 Effect.catchReason(
31 "UsersError",
32 "SearchQueryTooShort",
33 // Re-fail the "SearchQueryTooShort" reason
34 Effect.fail,
35 // All other reasons are unexpected, so we convert them into a 500
36 // Internal Server Error.
37 Effect.die
38 )
39 )
40 }),
41 getById: ({ params }) =>
42 users.getById(params.id).pipe(
43 // You can also use Effect.catchReasons to handle multiple error
44 // reasons at once
45 Effect.catchReasons("UsersError", {
46 UserNotFound: (e) => Effect.fail(e)
47 }, Effect.die)
48 ),
49 create: ({ payload }) =>
50 users.create(payload).pipe(
51 Effect.orDie
52 // You could alse use Effect.unwrapReason to moves rror reasons up to
53 // the top level, so you can handle them with Effect.catch or
54 // Effect.catchTag etc.
55 //
56 // Effect.unwrapReason("UsersError"),
57 // Effect.catchTags({
58 // UserNotFound: Effect.die,
59 // SearchQueryTooShort: Effect.die
60 // })
61 ),
62 update: ({ params, payload }) =>
63 users.update(params.id, payload).pipe(
64 Effect.catchReasons("UsersError", {
65 UserNotFound: (e) => Effect.fail(e)
66 }, Effect.die)
67 ),
68 me: () =>
69 // The Authorization middleware provides the CurrentUser service, so we
70 // can access it here.
71 CurrentUser
72 })
73 })
74)
75
76// The handlers with all dependencies provided, ready to serve. The SQL-backed
77// `Users.layer` keeps the database wiring out of the server entrypoint.
78export const UsersApiHandlers = UsersApiHandlersNoDeps.pipe(
79 Layer.provide([Users.layer, AuthorizationLayer])
80)