@@ -16,6 +16,7 @@ High-level declarative HTTP API for [effect-ts](https://github.com/Effect-TS).
16
16
- [ Example server] ( #example-server )
17
17
- [ Inputs] ( #inputs )
18
18
- [ Example] ( #example )
19
+ - [ Headers] ( #headers )
19
20
- [ Layers and services] ( #layers-and-services )
20
21
- [ Logging] ( #logging )
21
22
- [ Error handling] ( #error-handling )
@@ -32,6 +33,7 @@ Install using
32
33
``` bash
33
34
pnpm add effect-http
34
35
```
36
+
35
37
Bootstrap a simple API specification.
36
38
37
39
``` typescript
@@ -41,8 +43,8 @@ import { pipe } from "@effect/data/Function";
41
43
import * as Effect from " @effect/io/Effect" ;
42
44
import * as S from " @effect/schema/Schema" ;
43
45
44
- const responseSchema = S .struct ({ name: S .string });
45
- const query = { id: S .number };
46
+ const responseSchema = Schema .struct ({ name: Schema .string });
47
+ const query = { id: Schema .number };
46
48
47
49
const api = pipe (
48
50
Http .api ({ title: " Users API" }),
@@ -109,9 +111,9 @@ import * as Http from "effect-http";
109
111
110
112
import { pipe } from " @effect/data/Function" ;
111
113
import * as Effect from " @effect/io/Effect" ;
112
- import * as S from " @effect/schema/Schema" ;
114
+ import * as Schema from " @effect/schema/Schema" ;
113
115
114
- const responseSchema = S .struct ({ name: S .string });
116
+ const responseSchema = Schema .struct ({ name: Schema .string });
115
117
116
118
const api = pipe (
117
119
Http .api (),
@@ -134,25 +136,80 @@ Each endpoint can declare its inputs. Inputs can be passed as
134
136
- ` body ` - request body
135
137
- ` query ` - query parameters
136
138
- ` params ` - path parameters
139
+ - ` headers ` - request headers
137
140
138
- Inputs are specified as part of the schemas structure.
141
+ Inputs are specified as part of the schemas structure.
139
142
140
143
#### Example
141
144
142
145
``` typescript
143
- import * as Http from " ../src" ;
146
+ import * as Http from " effect-http" ;
147
+
148
+ import * as Schema from " @effect/schema/Schema" ;
144
149
145
150
const api = pipe (
146
151
Http .api ({ title: " My api" }),
147
152
Http .get (" stuff" , " /stuff/:param" , {
148
- response: S .struct ({ value: S .number }),
149
- body: S .struct ({ bodyField: S .array (S .string ) }),
150
- query: { query: S .string },
151
- params: { param: S .string },
153
+ response: Schema .struct ({ value: Schema .number }),
154
+ body: Schema .struct ({ bodyField: Schema .array (Schema .string ) }),
155
+ query: { query: Schema .string },
156
+ params: { param: Schema .string },
152
157
}),
153
158
);
154
159
```
155
160
161
+ ### Headers
162
+
163
+ Request headers are part of input schemas along with the request body or query parameters.
164
+ Their schema is specified similarly to query parameters and path parameters, i.e. using
165
+ an object mapping header name onto a schema. The example below shows an API with
166
+ a single endpoint ` /hello ` which expects a header ` X-Client-Id ` to be present.
167
+
168
+ ``` typescript
169
+ import * as Http from " effect-http" ;
170
+
171
+ import * as Schema from " @effect/schema/Schema" ;
172
+
173
+ const api = pipe (
174
+ Http .api (),
175
+ Http .get (" hello" , " /hello" , {
176
+ response: Schema .string ,
177
+ headers: { " X-Client-Id" : Schema .string },
178
+ }),
179
+ );
180
+ ```
181
+
182
+ Server implementation deals with the validation the usual way. For example, if we try
183
+ to call the endpoint without the header we will get the following error response.
184
+
185
+ ``` json
186
+ { "error" : " InvalidHeadersError" , "details" : " x-client-id is missing" }
187
+ ```
188
+
189
+ And as usual, the information about headers will be reflected in the generated
190
+ OpenApi UI.
191
+
192
+ ![ example-headers-openapi-ui] ( assets/example-headers-openapi-ui.png )
193
+
194
+ ** Important note** . You might have noticed the ` details ` field of the error response
195
+ describes the missing header using lower-case. This is not an error but rather a
196
+ consequence of the fact that HTTP headers are case-insensitive and internally ` effect-http `
197
+ converts all header names to lower-case to simplify integration with the underlying
198
+ http library - [ express] ( https://github.com/expressjs/express ) .
199
+
200
+ Don't worry, this is also encoded into the type information and if you were to
201
+ implement the handler, both autocompletion and type-checking would hint the
202
+ lower-cased form of the header name.
203
+
204
+ ``` typescript
205
+ const handleHello = ({
206
+ headers: { " x-client-id" : clientId },
207
+ }: Http .Input <typeof api , " hello" >) => Effect .succeed (" all good" );
208
+ ```
209
+
210
+ Take a look at [ examples/headers.ts] ( examples/headers.ts ) to see a complete example
211
+ API implementation with in-memory rate-limiting and client identification using headers.
212
+
156
213
### Layers and services
157
214
158
215
When constructing a ` Server ` implementation, the type system ensures all
@@ -211,9 +268,11 @@ functions we can use in the error rail of the handler effect.
211
268
- 400 ` Http.invalidQueryError `
212
269
- 400 ` Http.invalidParamsError `
213
270
- 400 ` Http.invalidBodyError `
271
+ - 400 ` Http.invalidHeadersError `
214
272
- 401 ` Http.unauthorizedError `
215
273
- 404 ` Http.notFoundError `
216
274
- 409 ` Http.conflictError `
275
+ - 429 ` Http.tooManyRequestsError `
217
276
218
277
##### 5xx
219
278
@@ -237,8 +296,8 @@ import * as S from "@effect/schema/Schema";
237
296
const api = pipe (
238
297
Http .api ({ title: " Users API" }),
239
298
Http .post (" storeUser" , " /users" , {
240
- response: S .string ,
241
- body: S .struct ({ name: S .string }),
299
+ response: Schema .string ,
300
+ body: Schema .struct ({ name: Schema .string }),
242
301
}),
243
302
);
244
303
```
@@ -349,7 +408,7 @@ import { pipe } from "@effect/data/Function";
349
408
import * as Effect from " @effect/io/Effect" ;
350
409
import * as S from " @effect/schema/Schema" ;
351
410
352
- const responseSchema = S .struct({ name: S .string });
411
+ const responseSchema = Schema .struct({ name: Schema .string });
353
412
354
413
const testApi = pipe(
355
414
Http.apiGroup(" test" ),
@@ -386,7 +445,7 @@ _(This is a complete standalone code example)_
386
445
387
446
The OpenApi UI groups endpoints using the specified groups.
388
447
389
- ! [example-generated-open-api-ui](assets/exmple -server-open-api .png)
448
+ ! [example-generated-open-api-ui](assets/example -server-openapi-ui .png)
390
449
391
450
# # Cookbook
392
451
@@ -409,8 +468,8 @@ import * as S from "@effect/schema/Schema";
409
468
const api = pipe(
410
469
Http.api(),
411
470
Http.get("stuff", "/stuff", {
412
- response: S .string,
413
- query: S .struct({ value: S .string }),
471
+ response: Schema .string,
472
+ query: Schema .struct({ value: Schema .string }),
414
473
}),
415
474
);
416
475
0 commit comments