@effect/platform 0.68.5 → 0.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/HttpApiMiddleware/package.json +6 -0
  2. package/README.md +306 -233
  3. package/dist/cjs/Headers.js +7 -2
  4. package/dist/cjs/Headers.js.map +1 -1
  5. package/dist/cjs/HttpApi.js +90 -78
  6. package/dist/cjs/HttpApi.js.map +1 -1
  7. package/dist/cjs/HttpApiBuilder.js +243 -255
  8. package/dist/cjs/HttpApiBuilder.js.map +1 -1
  9. package/dist/cjs/HttpApiClient.js +64 -59
  10. package/dist/cjs/HttpApiClient.js.map +1 -1
  11. package/dist/cjs/HttpApiEndpoint.js +74 -109
  12. package/dist/cjs/HttpApiEndpoint.js.map +1 -1
  13. package/dist/cjs/HttpApiError.js +3 -4
  14. package/dist/cjs/HttpApiError.js.map +1 -1
  15. package/dist/cjs/HttpApiGroup.js +103 -100
  16. package/dist/cjs/HttpApiGroup.js.map +1 -1
  17. package/dist/cjs/HttpApiMiddleware.js +67 -0
  18. package/dist/cjs/HttpApiMiddleware.js.map +1 -0
  19. package/dist/cjs/HttpApiSchema.js +33 -7
  20. package/dist/cjs/HttpApiSchema.js.map +1 -1
  21. package/dist/cjs/HttpApiSecurity.js +2 -2
  22. package/dist/cjs/HttpApiSecurity.js.map +1 -1
  23. package/dist/cjs/HttpApiSwagger.js +3 -1
  24. package/dist/cjs/HttpApiSwagger.js.map +1 -1
  25. package/dist/cjs/HttpBody.js.map +1 -1
  26. package/dist/cjs/HttpIncomingMessage.js +5 -1
  27. package/dist/cjs/HttpIncomingMessage.js.map +1 -1
  28. package/dist/cjs/HttpServer.js +12 -1
  29. package/dist/cjs/HttpServer.js.map +1 -1
  30. package/dist/cjs/HttpServerRespondable.js +1 -1
  31. package/dist/cjs/HttpServerRespondable.js.map +1 -1
  32. package/dist/cjs/OpenApi.js +102 -63
  33. package/dist/cjs/OpenApi.js.map +1 -1
  34. package/dist/cjs/OpenApiJsonSchema.js +58 -47
  35. package/dist/cjs/OpenApiJsonSchema.js.map +1 -1
  36. package/dist/cjs/Transferable.js +2 -2
  37. package/dist/cjs/Transferable.js.map +1 -1
  38. package/dist/cjs/UrlParams.js +5 -1
  39. package/dist/cjs/UrlParams.js.map +1 -1
  40. package/dist/cjs/Worker.js.map +1 -1
  41. package/dist/cjs/WorkerError.js +1 -5
  42. package/dist/cjs/WorkerError.js.map +1 -1
  43. package/dist/cjs/WorkerRunner.js.map +1 -1
  44. package/dist/cjs/index.js +3 -1
  45. package/dist/cjs/internal/httpBody.js +1 -1
  46. package/dist/cjs/internal/httpBody.js.map +1 -1
  47. package/dist/cjs/internal/httpClientRequest.js.map +1 -1
  48. package/dist/cjs/internal/httpClientResponse.js +1 -1
  49. package/dist/cjs/internal/httpClientResponse.js.map +1 -1
  50. package/dist/cjs/internal/httpRouter.js +1 -1
  51. package/dist/cjs/internal/httpRouter.js.map +1 -1
  52. package/dist/cjs/internal/httpServer.js +7 -1
  53. package/dist/cjs/internal/httpServer.js.map +1 -1
  54. package/dist/cjs/internal/httpServerRequest.js +1 -1
  55. package/dist/cjs/internal/httpServerRequest.js.map +1 -1
  56. package/dist/cjs/internal/httpServerResponse.js.map +1 -1
  57. package/dist/cjs/internal/keyValueStore.js +1 -1
  58. package/dist/cjs/internal/keyValueStore.js.map +1 -1
  59. package/dist/cjs/internal/multipart.js +1 -1
  60. package/dist/cjs/internal/multipart.js.map +1 -1
  61. package/dist/cjs/internal/worker.js +6 -7
  62. package/dist/cjs/internal/worker.js.map +1 -1
  63. package/dist/cjs/internal/workerRunner.js +3 -4
  64. package/dist/cjs/internal/workerRunner.js.map +1 -1
  65. package/dist/dts/Headers.d.ts +4 -6
  66. package/dist/dts/Headers.d.ts.map +1 -1
  67. package/dist/dts/HttpApi.d.ts +64 -140
  68. package/dist/dts/HttpApi.d.ts.map +1 -1
  69. package/dist/dts/HttpApiBuilder.d.ts +84 -167
  70. package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
  71. package/dist/dts/HttpApiClient.d.ts +34 -11
  72. package/dist/dts/HttpApiClient.d.ts.map +1 -1
  73. package/dist/dts/HttpApiEndpoint.d.ts +119 -273
  74. package/dist/dts/HttpApiEndpoint.d.ts.map +1 -1
  75. package/dist/dts/HttpApiError.d.ts +5 -2
  76. package/dist/dts/HttpApiError.d.ts.map +1 -1
  77. package/dist/dts/HttpApiGroup.d.ts +96 -194
  78. package/dist/dts/HttpApiGroup.d.ts.map +1 -1
  79. package/dist/dts/HttpApiMiddleware.d.ts +231 -0
  80. package/dist/dts/HttpApiMiddleware.d.ts.map +1 -0
  81. package/dist/dts/HttpApiSchema.d.ts +6 -2
  82. package/dist/dts/HttpApiSchema.d.ts.map +1 -1
  83. package/dist/dts/HttpApiSecurity.d.ts +1 -1
  84. package/dist/dts/HttpApiSecurity.d.ts.map +1 -1
  85. package/dist/dts/HttpApiSwagger.d.ts +2 -2
  86. package/dist/dts/HttpApiSwagger.d.ts.map +1 -1
  87. package/dist/dts/HttpBody.d.ts +2 -2
  88. package/dist/dts/HttpBody.d.ts.map +1 -1
  89. package/dist/dts/HttpClientRequest.d.ts +2 -2
  90. package/dist/dts/HttpClientRequest.d.ts.map +1 -1
  91. package/dist/dts/HttpClientResponse.d.ts +3 -3
  92. package/dist/dts/HttpClientResponse.d.ts.map +1 -1
  93. package/dist/dts/HttpIncomingMessage.d.ts +3 -3
  94. package/dist/dts/HttpIncomingMessage.d.ts.map +1 -1
  95. package/dist/dts/HttpRouter.d.ts +3 -3
  96. package/dist/dts/HttpRouter.d.ts.map +1 -1
  97. package/dist/dts/HttpServer.d.ts +15 -0
  98. package/dist/dts/HttpServer.d.ts.map +1 -1
  99. package/dist/dts/HttpServerRequest.d.ts +3 -3
  100. package/dist/dts/HttpServerRequest.d.ts.map +1 -1
  101. package/dist/dts/HttpServerRespondable.d.ts.map +1 -1
  102. package/dist/dts/HttpServerResponse.d.ts +2 -2
  103. package/dist/dts/HttpServerResponse.d.ts.map +1 -1
  104. package/dist/dts/KeyValueStore.d.ts +2 -2
  105. package/dist/dts/KeyValueStore.d.ts.map +1 -1
  106. package/dist/dts/Multipart.d.ts +3 -3
  107. package/dist/dts/Multipart.d.ts.map +1 -1
  108. package/dist/dts/OpenApi.d.ts +17 -39
  109. package/dist/dts/OpenApi.d.ts.map +1 -1
  110. package/dist/dts/OpenApiJsonSchema.d.ts +10 -5
  111. package/dist/dts/OpenApiJsonSchema.d.ts.map +1 -1
  112. package/dist/dts/Transferable.d.ts +4 -1
  113. package/dist/dts/Transferable.d.ts.map +1 -1
  114. package/dist/dts/UrlParams.d.ts +3 -6
  115. package/dist/dts/UrlParams.d.ts.map +1 -1
  116. package/dist/dts/Worker.d.ts +7 -8
  117. package/dist/dts/Worker.d.ts.map +1 -1
  118. package/dist/dts/WorkerError.d.ts +1 -1
  119. package/dist/dts/WorkerError.d.ts.map +1 -1
  120. package/dist/dts/WorkerRunner.d.ts +2 -3
  121. package/dist/dts/WorkerRunner.d.ts.map +1 -1
  122. package/dist/dts/index.d.ts +4 -0
  123. package/dist/dts/index.d.ts.map +1 -1
  124. package/dist/dts/internal/httpRouter.d.ts.map +1 -1
  125. package/dist/esm/Headers.js +7 -2
  126. package/dist/esm/Headers.js.map +1 -1
  127. package/dist/esm/HttpApi.js +88 -77
  128. package/dist/esm/HttpApi.js.map +1 -1
  129. package/dist/esm/HttpApiBuilder.js +236 -244
  130. package/dist/esm/HttpApiBuilder.js.map +1 -1
  131. package/dist/esm/HttpApiClient.js +64 -59
  132. package/dist/esm/HttpApiClient.js.map +1 -1
  133. package/dist/esm/HttpApiEndpoint.js +73 -106
  134. package/dist/esm/HttpApiEndpoint.js.map +1 -1
  135. package/dist/esm/HttpApiError.js +3 -4
  136. package/dist/esm/HttpApiError.js.map +1 -1
  137. package/dist/esm/HttpApiGroup.js +102 -99
  138. package/dist/esm/HttpApiGroup.js.map +1 -1
  139. package/dist/esm/HttpApiMiddleware.js +56 -0
  140. package/dist/esm/HttpApiMiddleware.js.map +1 -0
  141. package/dist/esm/HttpApiSchema.js +31 -5
  142. package/dist/esm/HttpApiSchema.js.map +1 -1
  143. package/dist/esm/HttpApiSecurity.js +1 -1
  144. package/dist/esm/HttpApiSecurity.js.map +1 -1
  145. package/dist/esm/HttpApiSwagger.js +4 -2
  146. package/dist/esm/HttpApiSwagger.js.map +1 -1
  147. package/dist/esm/HttpBody.js.map +1 -1
  148. package/dist/esm/HttpIncomingMessage.js +4 -1
  149. package/dist/esm/HttpIncomingMessage.js.map +1 -1
  150. package/dist/esm/HttpServer.js +11 -0
  151. package/dist/esm/HttpServer.js.map +1 -1
  152. package/dist/esm/HttpServerRespondable.js +1 -1
  153. package/dist/esm/HttpServerRespondable.js.map +1 -1
  154. package/dist/esm/OpenApi.js +97 -59
  155. package/dist/esm/OpenApi.js.map +1 -1
  156. package/dist/esm/OpenApiJsonSchema.js +56 -46
  157. package/dist/esm/OpenApiJsonSchema.js.map +1 -1
  158. package/dist/esm/Transferable.js +2 -2
  159. package/dist/esm/Transferable.js.map +1 -1
  160. package/dist/esm/UrlParams.js +4 -1
  161. package/dist/esm/UrlParams.js.map +1 -1
  162. package/dist/esm/Worker.js.map +1 -1
  163. package/dist/esm/WorkerError.js +1 -4
  164. package/dist/esm/WorkerError.js.map +1 -1
  165. package/dist/esm/WorkerRunner.js.map +1 -1
  166. package/dist/esm/index.js +4 -0
  167. package/dist/esm/index.js.map +1 -1
  168. package/dist/esm/internal/httpBody.js +1 -1
  169. package/dist/esm/internal/httpBody.js.map +1 -1
  170. package/dist/esm/internal/httpClientRequest.js.map +1 -1
  171. package/dist/esm/internal/httpClientResponse.js +1 -1
  172. package/dist/esm/internal/httpClientResponse.js.map +1 -1
  173. package/dist/esm/internal/httpRouter.js +1 -1
  174. package/dist/esm/internal/httpRouter.js.map +1 -1
  175. package/dist/esm/internal/httpServer.js +6 -0
  176. package/dist/esm/internal/httpServer.js.map +1 -1
  177. package/dist/esm/internal/httpServerRequest.js +1 -1
  178. package/dist/esm/internal/httpServerRequest.js.map +1 -1
  179. package/dist/esm/internal/httpServerResponse.js.map +1 -1
  180. package/dist/esm/internal/keyValueStore.js +1 -1
  181. package/dist/esm/internal/keyValueStore.js.map +1 -1
  182. package/dist/esm/internal/multipart.js +1 -1
  183. package/dist/esm/internal/multipart.js.map +1 -1
  184. package/dist/esm/internal/worker.js +6 -7
  185. package/dist/esm/internal/worker.js.map +1 -1
  186. package/dist/esm/internal/workerRunner.js +3 -4
  187. package/dist/esm/internal/workerRunner.js.map +1 -1
  188. package/package.json +10 -3
  189. package/src/Headers.ts +12 -4
  190. package/src/HttpApi.ts +183 -258
  191. package/src/HttpApiBuilder.ts +532 -481
  192. package/src/HttpApiClient.ts +163 -112
  193. package/src/HttpApiEndpoint.ts +443 -564
  194. package/src/HttpApiError.ts +4 -6
  195. package/src/HttpApiGroup.ts +277 -325
  196. package/src/HttpApiMiddleware.ts +318 -0
  197. package/src/HttpApiSchema.ts +39 -2
  198. package/src/HttpApiSecurity.ts +1 -1
  199. package/src/HttpApiSwagger.ts +3 -3
  200. package/src/HttpBody.ts +2 -2
  201. package/src/HttpClientRequest.ts +2 -2
  202. package/src/HttpClientResponse.ts +3 -3
  203. package/src/HttpIncomingMessage.ts +3 -3
  204. package/src/HttpRouter.ts +3 -3
  205. package/src/HttpServer.ts +21 -0
  206. package/src/HttpServerRequest.ts +3 -3
  207. package/src/HttpServerRespondable.ts +1 -1
  208. package/src/HttpServerResponse.ts +2 -2
  209. package/src/KeyValueStore.ts +2 -2
  210. package/src/Multipart.ts +3 -3
  211. package/src/OpenApi.ts +113 -104
  212. package/src/OpenApiJsonSchema.ts +67 -53
  213. package/src/Transferable.ts +2 -2
  214. package/src/UrlParams.ts +3 -3
  215. package/src/Worker.ts +7 -8
  216. package/src/WorkerError.ts +1 -1
  217. package/src/WorkerRunner.ts +2 -3
  218. package/src/index.ts +5 -0
  219. package/src/internal/httpBody.ts +2 -2
  220. package/src/internal/httpClientRequest.ts +2 -2
  221. package/src/internal/httpClientResponse.ts +3 -3
  222. package/src/internal/httpRouter.ts +2 -2
  223. package/src/internal/httpServer.ts +13 -0
  224. package/src/internal/httpServerRequest.ts +3 -3
  225. package/src/internal/httpServerResponse.ts +2 -2
  226. package/src/internal/keyValueStore.ts +1 -1
  227. package/src/internal/multipart.ts +3 -3
  228. package/src/internal/worker.ts +6 -7
  229. package/src/internal/workerRunner.ts +3 -4
package/README.md CHANGED
@@ -32,7 +32,7 @@ Let's define a simple CRUD API for managing users. First, we need to make an
32
32
 
33
33
  ```ts
34
34
  import { HttpApiEndpoint, HttpApiGroup } from "@effect/platform"
35
- import { Schema } from "@effect/schema"
35
+ import { Schema } from "effect"
36
36
 
37
37
  // Our domain "User" Schema
38
38
  class User extends Schema.Class<User>("User")({
@@ -41,58 +41,52 @@ class User extends Schema.Class<User>("User")({
41
41
  createdAt: Schema.DateTimeUtc
42
42
  }) {}
43
43
 
44
- const usersApi = HttpApiGroup.make("users").pipe(
45
- HttpApiGroup.add(
44
+ const usersApi = HttpApiGroup.make("users")
45
+ .add(
46
46
  // each endpoint has a name and a path
47
- HttpApiEndpoint.get("findById", "/users/:id").pipe(
47
+ HttpApiEndpoint.get("findById", "/users/:id")
48
48
  // the endpoint can have a Schema for a successful response
49
- HttpApiEndpoint.setSuccess(User),
49
+ .addSuccess(User)
50
50
  // and here is a Schema for the path parameters
51
- HttpApiEndpoint.setPath(
51
+ .setPath(
52
52
  Schema.Struct({
53
53
  id: Schema.NumberFromString
54
54
  })
55
55
  )
56
- )
57
- ),
58
- HttpApiGroup.add(
59
- HttpApiEndpoint.post("create", "/users").pipe(
60
- HttpApiEndpoint.setSuccess(User),
56
+ )
57
+ .add(
58
+ HttpApiEndpoint.post("create", "/users")
59
+ .addSuccess(User)
61
60
  // and here is a Schema for the request payload / body
62
61
  //
63
62
  // this is a POST request, so the payload is in the body
64
63
  // but for a GET request, the payload would be in the URL search params
65
- HttpApiEndpoint.setPayload(
64
+ .setPayload(
66
65
  Schema.Struct({
67
66
  name: Schema.String
68
67
  })
69
68
  )
70
- )
71
- ),
69
+ )
72
70
  // by default, the endpoint will respond with a 204 No Content
73
- HttpApiGroup.add(HttpApiEndpoint.del("delete", "/users/:id")),
74
- HttpApiGroup.add(
75
- HttpApiEndpoint.patch("update", "/users/:id").pipe(
76
- HttpApiEndpoint.setSuccess(User),
77
- HttpApiEndpoint.setPayload(
71
+ .add(HttpApiEndpoint.del("delete", "/users/:id"))
72
+ .add(
73
+ HttpApiEndpoint.patch("update", "/users/:id")
74
+ .addSuccess(User)
75
+ .setPayload(
78
76
  Schema.Struct({
79
77
  name: Schema.String
80
78
  })
81
79
  )
82
- )
83
80
  )
84
- )
85
81
  ```
86
82
 
87
83
  You can also extend the `HttpApiGroup` with a class to gain an opaque type.
88
84
  We will use this API style in the following examples:
89
85
 
90
86
  ```ts
91
- class UsersApi extends HttpApiGroup.make("users").pipe(
92
- HttpApiGroup.add(
93
- HttpApiEndpoint.get("findById", "/users/:id")
94
- // ... same as above
95
- )
87
+ class UsersApi extends HttpApiGroup.make("users").add(
88
+ HttpApiEndpoint.get("findById", "/users/:id")
89
+ // ... same as above
96
90
  ) {}
97
91
  ```
98
92
 
@@ -103,13 +97,13 @@ Once you have defined your groups, you can combine them into a single `HttpApi`.
103
97
  ```ts
104
98
  import { HttpApi } from "@effect/platform"
105
99
 
106
- class MyApi extends HttpApi.empty.pipe(HttpApi.addGroup(UsersApi)) {}
100
+ class MyApi extends HttpApi.empty.add(UsersApi) {}
107
101
  ```
108
102
 
109
103
  Or with the non-opaque style:
110
104
 
111
105
  ```ts
112
- const api = HttpApi.empty.pipe(HttpApi.addGroup(usersApi))
106
+ const api = HttpApi.empty.add(usersApi)
113
107
  ```
114
108
 
115
109
  ### Adding OpenApi annotations
@@ -121,17 +115,21 @@ Let's add a title to our `UsersApi` group:
121
115
  ```ts
122
116
  import { OpenApi } from "@effect/platform"
123
117
 
124
- class UsersApi extends HttpApiGroup.make("users").pipe(
125
- HttpApiGroup.add(
118
+ class UsersApi extends HttpApiGroup.make("users")
119
+ .add(
126
120
  HttpApiEndpoint.get("findById", "/users/:id")
127
121
  // ... same as above
128
- ),
122
+ )
129
123
  // add an OpenApi title & description
130
- OpenApi.annotate({
131
- title: "Users API",
132
- description: "API for managing users"
133
- })
134
- ) {}
124
+ // You can set one attribute at a time
125
+ .annotate(OpenApi.Title, "Users API")
126
+ // or multiple at once
127
+ .annotateContext(
128
+ OpenApi.annotations({
129
+ title: "Users API",
130
+ description: "API for managing users"
131
+ })
132
+ ) {}
135
133
  ```
136
134
 
137
135
  Now when you generate OpenApi documentation, the title and description will be
@@ -140,13 +138,9 @@ included.
140
138
  You can also add OpenApi annotations to the top-level `HttpApi`:
141
139
 
142
140
  ```ts
143
- class MyApi extends HttpApi.empty.pipe(
144
- HttpApi.addGroup(UsersApi),
145
- OpenApi.annotate({
146
- title: "My API",
147
- description: "My awesome API"
148
- })
149
- ) {}
141
+ class MyApi extends HttpApi.empty
142
+ .add(UsersApi)
143
+ .annotate(OpenApi.Title, "My API") {}
150
144
  ```
151
145
 
152
146
  ### Adding errors
@@ -174,18 +168,16 @@ class Unauthorized extends Schema.TaggedError<Unauthorized>()(
174
168
  {}
175
169
  ) {}
176
170
 
177
- class UsersApi extends HttpApiGroup.make("users").pipe(
178
- HttpApiGroup.add(
179
- HttpApiEndpoint.get("findById", "/users/:id").pipe(
171
+ class UsersApi extends HttpApiGroup.make("users")
172
+ .add(
173
+ HttpApiEndpoint.get("findById", "/users/:id")
180
174
  // here we are adding our error response
181
- HttpApiEndpoint.addError(UserNotFound, { status: 404 }),
182
- HttpApiEndpoint.setSuccess(User),
183
- HttpApiEndpoint.setPath(Schema.Struct({ id: Schema.NumberFromString }))
184
- )
185
- ),
175
+ .addError(UserNotFound, { status: 404 })
176
+ .addSuccess(User)
177
+ .setPath(Schema.Struct({ id: Schema.NumberFromString }))
178
+ )
186
179
  // or we could add an error to the group
187
- HttpApiGroup.addError(Unauthorized, { status: 401 })
188
- ) {}
180
+ .addError(Unauthorized, { status: 401 }) {}
189
181
  ```
190
182
 
191
183
  It is worth noting that you can add multiple error responses to an endpoint,
@@ -202,62 +194,18 @@ shape of the multipart request.
202
194
  ```ts
203
195
  import { HttpApiSchema, Multipart } from "@effect/platform"
204
196
 
205
- class UsersApi extends HttpApiGroup.make("users").pipe(
206
- HttpApiGroup.add(
207
- HttpApiEndpoint.post("upload", "/users/upload").pipe(
208
- HttpApiEndpoint.setPayload(
209
- HttpApiSchema.Multipart(
210
- Schema.Struct({
211
- // add a "files" field to the schema
212
- files: Multipart.FilesSchema
213
- })
214
- )
215
- )
197
+ class UsersApi extends HttpApiGroup.make("users").add(
198
+ HttpApiEndpoint.post("upload", "/users/upload").setPayload(
199
+ HttpApiSchema.Multipart(
200
+ Schema.Struct({
201
+ // add a "files" field to the schema
202
+ files: Multipart.FilesSchema
203
+ })
216
204
  )
217
205
  )
218
206
  ) {}
219
207
  ```
220
208
 
221
- ### Adding security annotations
222
-
223
- The `HttpApiSecurity` module provides a way to add security annotations to your
224
- API.
225
-
226
- It offers the following authorization types:
227
-
228
- - `HttpApiSecurity.apiKey` - API key authorization through headers, query
229
- parameters, or cookies.
230
- - `HttpApiSecurity.basicAuth` - HTTP Basic authentication.
231
- - `HttpApiSecurity.bearerAuth` - Bearer token authentication.
232
-
233
- You can annotate your API with these security types using the
234
- `OpenApi.annotate` api as before.
235
-
236
- ```ts
237
- import { HttpApiSecurity } from "@effect/platform"
238
-
239
- const security = HttpApiSecurity.apiKey({
240
- in: "cookie",
241
- key: "token"
242
- })
243
-
244
- class UsersApi extends HttpApiGroup.make("users").pipe(
245
- HttpApiGroup.add(
246
- HttpApiEndpoint.get("findById", "/users/:id").pipe(
247
- // add the security annotation to the endpoint
248
- OpenApi.annotate({ security })
249
- )
250
- ),
251
- // or at the group level
252
- OpenApi.annotate({ security }),
253
-
254
- // or just for the endpoints above this line
255
- HttpApiGroup.annotateEndpoints(OpenApi.Security, security),
256
- // this endpoint will not have the security annotation
257
- HttpApiGroup.add(HttpApiEndpoint.get("list", "/users"))
258
- ) {}
259
- ```
260
-
261
209
  ### Changing the response encoding
262
210
 
263
211
  By default, the response is encoded as JSON. You can change the encoding using
@@ -266,17 +214,13 @@ the `HttpApiSchema.withEncoding` api.
266
214
  Here is an example of changing the encoding to text/csv:
267
215
 
268
216
  ```ts
269
- class UsersApi extends HttpApiGroup.make("users").pipe(
270
- HttpApiGroup.add(
271
- HttpApiEndpoint.get("csv", "/users/csv").pipe(
272
- HttpApiEndpoint.setSuccess(
273
- Schema.String.pipe(
274
- HttpApiSchema.withEncoding({
275
- kind: "Text",
276
- contentType: "text/csv"
277
- })
278
- )
279
- )
217
+ class UsersApi extends HttpApiGroup.make("users").add(
218
+ HttpApiEndpoint.get("csv", "/users/csv").addSuccess(
219
+ Schema.String.pipe(
220
+ HttpApiSchema.withEncoding({
221
+ kind: "Text",
222
+ contentType: "text/csv"
223
+ })
280
224
  )
281
225
  )
282
226
  ) {}
@@ -306,8 +250,7 @@ import {
306
250
  HttpApiEndpoint,
307
251
  HttpApiGroup
308
252
  } from "@effect/platform"
309
- import { Schema } from "@effect/schema"
310
- import { DateTime, Effect } from "effect"
253
+ import { DateTime, Effect, Layer, Schema } from "effect"
311
254
 
312
255
  // here is our api definition
313
256
  class User extends Schema.Class<User>("User")({
@@ -316,31 +259,28 @@ class User extends Schema.Class<User>("User")({
316
259
  createdAt: Schema.DateTimeUtc
317
260
  }) {}
318
261
 
319
- class UsersApi extends HttpApiGroup.make("users").pipe(
320
- HttpApiGroup.add(
321
- HttpApiEndpoint.get("findById", "/users/:id").pipe(
322
- HttpApiEndpoint.setSuccess(User),
323
- HttpApiEndpoint.setPath(
324
- Schema.Struct({
325
- id: Schema.NumberFromString
326
- })
327
- )
262
+ class UsersApi extends HttpApiGroup.make("users").add(
263
+ HttpApiEndpoint.get("findById", "/users/:id")
264
+ .addSuccess(User)
265
+ .setPath(
266
+ Schema.Struct({
267
+ id: Schema.NumberFromString
268
+ })
328
269
  )
329
- )
330
270
  ) {}
331
271
 
332
- class MyApi extends HttpApi.empty.pipe(HttpApi.addGroup(UsersApi)) {}
272
+ class MyApi extends HttpApi.empty.add(UsersApi) {}
333
273
 
334
274
  // --------------------------------------------
335
275
  // Implementation
336
276
  // --------------------------------------------
337
277
 
338
278
  // the `HttpApiBuilder.group` api returns a `Layer`
339
- const UsersApiLive: Layer.Layer<HttpApiGroup.HttpApiGroup.Service<"users">> =
279
+ const UsersApiLive: Layer.Layer<HttpApiGroup.Group<"users">> =
340
280
  HttpApiBuilder.group(MyApi, "users", (handlers) =>
341
- handlers.pipe(
281
+ handlers
342
282
  // the parameters & payload are passed to the handler function.
343
- HttpApiBuilder.handle("findById", ({ path: { id } }) =>
283
+ .handle("findById", ({ path: { id } }) =>
344
284
  Effect.succeed(
345
285
  new User({
346
286
  id,
@@ -349,7 +289,6 @@ const UsersApiLive: Layer.Layer<HttpApiGroup.HttpApiGroup.Service<"users">> =
349
289
  })
350
290
  )
351
291
  )
352
- )
353
292
  )
354
293
  ```
355
294
 
@@ -368,17 +307,15 @@ class UsersRepository extends Context.Tag("UsersRepository")<
368
307
 
369
308
  // the dependencies will show up in the resulting `Layer`
370
309
  const UsersApiLive: Layer.Layer<
371
- HttpApiGroup.HttpApiGroup.Service<"users">,
310
+ HttpApiGroup.Group<"users">,
372
311
  never,
373
312
  UsersRepository
374
313
  > = HttpApiBuilder.group(MyApi, "users", (handlers) =>
375
314
  // we can return an Effect that creates our handlers
376
315
  Effect.gen(function* () {
377
316
  const repository = yield* UsersRepository
378
- return handlers.pipe(
379
- HttpApiBuilder.handle("findById", ({ path: { id } }) =>
380
- repository.findById(id)
381
- )
317
+ return handlers.handle("findById", ({ path: { id } }) =>
318
+ repository.findById(id)
382
319
  )
383
320
  })
384
321
  )
@@ -392,9 +329,9 @@ This is done using the `HttpApiBuilder.api` api, and then using `Layer.provide`
392
329
  to add all the group implementations.
393
330
 
394
331
  ```ts
395
- const MyApiLive: Layer.Layer<HttpApi.HttpApi.Service> = HttpApiBuilder.api(
396
- MyApi
397
- ).pipe(Layer.provide(UsersApiLive))
332
+ const MyApiLive: Layer.Layer<HttpApi.Api> = HttpApiBuilder.api(MyApi).pipe(
333
+ Layer.provide(UsersApiLive)
334
+ )
398
335
  ```
399
336
 
400
337
  ### Serving the API
@@ -426,71 +363,241 @@ const HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
426
363
  Layer.launch(HttpLive).pipe(NodeRuntime.runMain)
427
364
  ```
428
365
 
429
- ## Implementing `HttpApiSecurity`
366
+ ### Serving Swagger documentation
430
367
 
431
- If you are using `HttpApiSecurity` in your API, you can use the security
432
- definition to implement a middleware that will protect your endpoints.
368
+ You can add Swagger documentation to your API using the `HttpApiSwagger` module.
433
369
 
434
- The `HttpApiBuilder.middlewareSecurity` api will assist you in creating this
435
- middleware.
370
+ You just need to provide the `HttpApiSwagger.layer` to your server
371
+ implementation:
436
372
 
437
- Here is an example:
373
+ ```ts
374
+ import { HttpApiSwagger } from "@effect/platform"
375
+
376
+ const HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
377
+ // add the swagger documentation layer
378
+ Layer.provide(
379
+ HttpApiSwagger.layer({
380
+ // "/docs" is the default path for the swagger documentation
381
+ path: "/docs"
382
+ })
383
+ ),
384
+ Layer.provide(HttpApiBuilder.middlewareCors()),
385
+ Layer.provide(MyApiLive),
386
+ Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))
387
+ )
388
+ ```
389
+
390
+ ## Adding middleware
391
+
392
+ ### Defining middleware
393
+
394
+ The `HttpApiMiddleware` module provides a way to add middleware to your API.
395
+
396
+ You can create a `HttpApiMiddleware.Tag` that represents your middleware, which
397
+ allows you to set:
398
+
399
+ - `failure` - a Schema for any errors that the middleware can return
400
+ - `provides` - a `Context.Tag` that the middleware will provide
401
+ - `security` - `HttpApiSecurity` definitions that the middleware will
402
+ implement
403
+ - `optional` - a boolean that indicates that if the middleware fails with an
404
+ expected error, the request should continue. When using optional middleware,
405
+ `provides` & `failure` options will not affect the handlers or final error type.
406
+
407
+ Here is an example of defining a simple logger middleware:
438
408
 
439
409
  ```ts
440
- // our cookie security definition
441
- const security = HttpApiSecurity.apiKey({
442
- in: "cookie",
443
- key: "token"
444
- })
410
+ import {
411
+ HttpApiEndpoint,
412
+ HttpApiGroup,
413
+ HttpApiMiddleware
414
+ } from "@effect/platform"
415
+ import { Schema } from "effect"
445
416
 
446
- // the user repository service
447
- class UsersRepository extends Context.Tag("UsersRepository")<
448
- UsersRepository,
449
- {
450
- readonly findByToken: (token: Redacted.Redacted) => Effect.Effect<User>
451
- }
452
- >() {}
417
+ class LoggerError extends Schema.TaggedError<LoggerError>()(
418
+ "LoggerError",
419
+ {}
420
+ ) {}
421
+
422
+ // first extend the HttpApiMiddleware.Tag class
423
+ class Logger extends HttpApiMiddleware.Tag<Logger>()("Http/Logger", {
424
+ // optionally define any errors that the middleware can return
425
+ failure: LoggerError
426
+ }) {}
427
+
428
+ // apply the middleware to an `HttpApiGroup`
429
+ class UsersApi extends HttpApiGroup.make("users")
430
+ .add(
431
+ HttpApiEndpoint.get("findById", "/:id")
432
+ // apply the middleware to a single endpoint
433
+ .middleware(Logger)
434
+ )
435
+ // or apply the middleware to the group
436
+ .middleware(Logger) {}
437
+ ```
438
+
439
+ ### Defining security middleware
440
+
441
+ The `HttpApiSecurity` module provides a way to add security annotations to your
442
+ API.
443
+
444
+ It offers the following authorization types:
445
+
446
+ - `HttpApiSecurity.apiKey` - API key authorization through headers, query
447
+ parameters, or cookies.
448
+ - `HttpApiSecurity.basicAuth` - HTTP Basic authentication.
449
+ - `HttpApiSecurity.bearerAuth` - Bearer token authentication.
450
+
451
+ You can then use these security annotations in combination with `HttpApiMiddleware`
452
+ to define middleware that will protect your endpoints.
453
+
454
+ ```ts
455
+ import {
456
+ HttpApiGroup,
457
+ HttpApiEndpoint,
458
+ HttpApiMiddleware,
459
+ HttpApiSchema,
460
+ HttpApiSecurity
461
+ } from "@effect/platform"
462
+ import { Context, Schema } from "effect"
463
+
464
+ class User extends Schema.Class<User>("User")({ id: Schema.Number }) {}
465
+
466
+ class Unauthorized extends Schema.TaggedError<Unauthorized>()(
467
+ "Unauthorized",
468
+ {},
469
+ HttpApiSchema.annotations({ status: 401 })
470
+ ) {}
453
471
 
454
- // the security middleware will supply the current user to the handlers
455
472
  class CurrentUser extends Context.Tag("CurrentUser")<CurrentUser, User>() {}
456
473
 
457
- // implement the middleware
458
- const makeSecurityMiddleware: Effect.Effect<
459
- HttpApiBuilder.SecurityMiddleware<CurrentUser>,
460
- never,
461
- UsersRepository
462
- > = Effect.gen(function* () {
463
- const repository = yield* UsersRepository
464
- return HttpApiBuilder.middlewareSecurity(
465
- // the security definition
466
- security,
467
- // the Context.Tag this middleware will provide
468
- CurrentUser,
469
- // the function to get the user from the token
470
- (token) => repository.findByToken(token)
474
+ // first extend the HttpApiMiddleware.Tag class
475
+ class Authorization extends HttpApiMiddleware.Tag<Authorization>()(
476
+ "Authorization",
477
+ {
478
+ // add your error schema
479
+ failure: Unauthorized,
480
+ // add the Context.Tag that the middleware will provide
481
+ provides: CurrentUser,
482
+ // add the security definitions
483
+ security: {
484
+ // the object key is a custom name for the security definition
485
+ myBearer: HttpApiSecurity.bearer
486
+ // You can add more security definitions here.
487
+ // They will attempt to be resolved in the order they are defined
488
+ }
489
+ }
490
+ ) {}
491
+
492
+ // apply the middleware to an `HttpApiGroup`
493
+ class UsersApi extends HttpApiGroup.make("users")
494
+ .add(
495
+ HttpApiEndpoint.get("findById", "/:id")
496
+ // apply the middleware to a single endpoint
497
+ .middleware(Authorization)
471
498
  )
472
- })
499
+ // or apply the middleware to the group
500
+ .middleware(Authorization) {}
501
+ ```
473
502
 
474
- // use the middleware
475
- const UsersApiLive = HttpApiBuilder.group(MyApi, "users", (handlers) =>
476
- Effect.gen(function* () {
477
- // construct the security middleware
478
- const securityMiddleware = yield* makeSecurityMiddleware
503
+ ### Implementing `HttpApiMiddleware`
479
504
 
480
- return handlers.pipe(
481
- HttpApiBuilder.handle("findById", ({ path: { id } }) =>
482
- Effect.succeed(
483
- new User({ id, name: "John Doe", createdAt: DateTime.unsafeNow() })
484
- )
485
- ),
486
- // apply the middleware to the findById endpoint
487
- securityMiddleware
488
- // any endpoint after this will not be protected
505
+ Once your `HttpApiMiddleware` is defined, you can use the
506
+ `HttpApiMiddleware.Tag` definition to implement your middleware.
507
+
508
+ By using the `Layer` apis, you can create a Layer that implements your
509
+ middleware.
510
+
511
+ Here is an example:
512
+
513
+ ```ts
514
+ import { HttpApiMiddleware, HttpServerRequest } from "@effect/platform"
515
+ import { Effect, Layer } from "effect"
516
+
517
+ class Logger extends HttpApiMiddleware.Tag<Logger>()("Http/Logger") {}
518
+
519
+ const LoggerLive = Layer.effect(
520
+ Logger,
521
+ Effect.gen(function* () {
522
+ yield* Effect.log("creating Logger middleware")
523
+
524
+ // standard middleware is just an Effect, that can access the `HttpRouter`
525
+ // context.
526
+ return Logger.of(
527
+ Effect.gen(function* () {
528
+ const request = yield* HttpServerRequest.HttpServerRequest
529
+ yield* Effect.log(`Request: ${request.method} ${request.url}`)
530
+ })
489
531
  )
490
532
  })
491
533
  )
492
534
  ```
493
535
 
536
+ When the `Layer` is created, you can then provide it to your group layers:
537
+
538
+ ```ts
539
+ const UsersApiLive = HttpApiBuilder.group(...).pipe(
540
+ Layer.provide(LoggerLive)
541
+ )
542
+ ```
543
+
544
+ ### Implementing `HttpApiSecurity` middleware
545
+
546
+ If you are using `HttpApiSecurity` in your middleware, implementing the `Layer`
547
+ looks a bit different.
548
+
549
+ Here is an example of implementing a `HttpApiSecurity.bearer` middleware:
550
+
551
+ ```ts
552
+ import {
553
+ HttpApiMiddleware,
554
+ HttpApiSchema,
555
+ HttpApiSecurity
556
+ } from "@effect/platform"
557
+ import { Context, Effect, Layer, Redacted, Schema } from "effect"
558
+
559
+ class User extends Schema.Class<User>("User")({ id: Schema.Number }) {}
560
+
561
+ class Unauthorized extends Schema.TaggedError<Unauthorized>()(
562
+ "Unauthorized",
563
+ {},
564
+ HttpApiSchema.annotations({ status: 401 })
565
+ ) {}
566
+
567
+ class CurrentUser extends Context.Tag("CurrentUser")<CurrentUser, User>() {}
568
+
569
+ class Authorization extends HttpApiMiddleware.Tag<Authorization>()(
570
+ "Authorization",
571
+ {
572
+ failure: Unauthorized,
573
+ provides: CurrentUser,
574
+ security: { myBearer: HttpApiSecurity.bearer }
575
+ }
576
+ ) {}
577
+
578
+ const AuthorizationLive = Layer.effect(
579
+ Authorization,
580
+ Effect.gen(function* () {
581
+ yield* Effect.log("creating Authorization middleware")
582
+
583
+ // return the security handlers
584
+ return Authorization.of({
585
+ myBearer: (bearerToken) =>
586
+ Effect.gen(function* () {
587
+ yield* Effect.log(
588
+ "checking bearer token",
589
+ Redacted.value(bearerToken)
590
+ )
591
+ // return the `User` that will be provided as the `CurrentUser`
592
+ return new User({ id: 1 })
593
+ })
594
+ })
595
+ })
596
+ )
597
+ ```
598
+
599
+ ### Setting `HttpApiSecurity` cookies
600
+
494
601
  If you need to set the security cookie from within a handler, you can use the
495
602
  `HttpApiBuilder.securitySetCookie` api.
496
603
 
@@ -503,42 +610,13 @@ const security = HttpApiSecurity.apiKey({
503
610
  })
504
611
 
505
612
  const UsersApiLive = HttpApiBuilder.group(MyApi, "users", (handlers) =>
506
- handlers.pipe(
507
- HttpApiBuilder.handle("login", () =>
508
- // set the security cookie
509
- HttpApiBuilder.securitySetCookie(
510
- security,
511
- Redacted.make("keep me secret")
512
- )
513
- )
613
+ handlers.handle("login", () =>
614
+ // set the security cookie
615
+ HttpApiBuilder.securitySetCookie(security, Redacted.make("keep me secret"))
514
616
  )
515
617
  )
516
618
  ```
517
619
 
518
- ### Serving Swagger documentation
519
-
520
- You can add Swagger documentation to your API using the `HttpApiSwagger` module.
521
-
522
- You just need to provide the `HttpApiSwagger.layer` to your server
523
- implementation:
524
-
525
- ```ts
526
- import { HttpApiSwagger } from "@effect/platform"
527
-
528
- const HttpLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
529
- // add the swagger documentation layer
530
- Layer.provide(
531
- HttpApiSwagger.layer({
532
- // "/docs" is the default path for the swagger documentation
533
- path: "/docs"
534
- })
535
- ),
536
- Layer.provide(HttpApiBuilder.middlewareCors()),
537
- Layer.provide(MyApiLive),
538
- Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))
539
- )
540
- ```
541
-
542
620
  ## Deriving a client
543
621
 
544
622
  Once you have defined your API, you can derive a client that can interact with
@@ -1161,7 +1239,7 @@ string {
1161
1239
 
1162
1240
  ### Decoding Data with Schemas
1163
1241
 
1164
- A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `@effect/schema`.
1242
+ A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`.
1165
1243
 
1166
1244
  ```ts
1167
1245
  import {
@@ -1170,8 +1248,7 @@ import {
1170
1248
  HttpClientResponse
1171
1249
  } from "@effect/platform"
1172
1250
  import { NodeRuntime } from "@effect/platform-node"
1173
- import { Schema } from "@effect/schema"
1174
- import { Console, Effect } from "effect"
1251
+ import { Console, Effect, Schema } from "effect"
1175
1252
 
1176
1253
  const Post = Schema.Struct({
1177
1254
  id: Schema.Number,
@@ -1329,7 +1406,7 @@ Output:
1329
1406
 
1330
1407
  ### Decoding Data with Schemas
1331
1408
 
1332
- A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `@effect/schema`.
1409
+ A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`.
1333
1410
 
1334
1411
  ```ts
1335
1412
  import {
@@ -1339,8 +1416,7 @@ import {
1339
1416
  HttpClientResponse
1340
1417
  } from "@effect/platform"
1341
1418
  import { NodeRuntime } from "@effect/platform-node"
1342
- import { Schema } from "@effect/schema"
1343
- import { Console, Effect } from "effect"
1419
+ import { Console, Effect, Schema } from "effect"
1344
1420
 
1345
1421
  const Post = Schema.Struct({
1346
1422
  id: Schema.Number,
@@ -1717,8 +1793,7 @@ To define routes with parameters, include the parameter names in the path and us
1717
1793
 
1718
1794
  ```ts
1719
1795
  import { HttpRouter, HttpServer, HttpServerResponse } from "@effect/platform"
1720
- import { Schema } from "@effect/schema"
1721
- import { Effect } from "effect"
1796
+ import { Effect, Schema } from "effect"
1722
1797
  import { listen } from "./listen.js"
1723
1798
 
1724
1799
  // Define the schema for route parameters
@@ -2408,7 +2483,7 @@ curl -i http://localhost:3000/fail
2408
2483
 
2409
2484
  ## Validations
2410
2485
 
2411
- Validation is a critical aspect of handling HTTP requests to ensure that the data your server receives is as expected. We'll explore how to validate headers and cookies using the `@effect/platform` and `@effect/schema` libraries, which provide structured and robust methods for these tasks.
2486
+ Validation is a critical aspect of handling HTTP requests to ensure that the data your server receives is as expected. We'll explore how to validate headers and cookies using the `@effect/platform` and `effect/Schema` libraries, which provide structured and robust methods for these tasks.
2412
2487
 
2413
2488
  ### Headers
2414
2489
 
@@ -2421,8 +2496,7 @@ import {
2421
2496
  HttpServerRequest,
2422
2497
  HttpServerResponse
2423
2498
  } from "@effect/platform"
2424
- import { Schema } from "@effect/schema"
2425
- import { Effect } from "effect"
2499
+ import { Effect, Schema } from "effect"
2426
2500
  import { listen } from "./listen.js"
2427
2501
 
2428
2502
  const router = HttpRouter.empty.pipe(
@@ -2472,8 +2546,7 @@ import {
2472
2546
  HttpServerRequest,
2473
2547
  HttpServerResponse
2474
2548
  } from "@effect/platform"
2475
- import { Schema } from "@effect/schema"
2476
- import { Effect } from "effect"
2549
+ import { Effect, Schema } from "effect"
2477
2550
  import { listen } from "./listen.js"
2478
2551
 
2479
2552
  const router = HttpRouter.empty.pipe(