Skip to content

Commit e099ee8

Browse files
committed
docs: update readme
1 parent 11e3874 commit e099ee8

File tree

3 files changed

+75
-16
lines changed

3 files changed

+75
-16
lines changed

assets/example-headers-openapi-ui.png

56.9 KB
Loading

readme.md

+75-16
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ High-level declarative HTTP API for [effect-ts](https://github.com/Effect-TS).
1616
- [Example server](#example-server)
1717
- [Inputs](#inputs)
1818
- [Example](#example)
19+
- [Headers](#headers)
1920
- [Layers and services](#layers-and-services)
2021
- [Logging](#logging)
2122
- [Error handling](#error-handling)
@@ -32,6 +33,7 @@ Install using
3233
```bash
3334
pnpm add effect-http
3435
```
36+
3537
Bootstrap a simple API specification.
3638

3739
```typescript
@@ -41,8 +43,8 @@ import { pipe } from "@effect/data/Function";
4143
import * as Effect from "@effect/io/Effect";
4244
import * as S from "@effect/schema/Schema";
4345

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 };
4648

4749
const api = pipe(
4850
Http.api({ title: "Users API" }),
@@ -109,9 +111,9 @@ import * as Http from "effect-http";
109111

110112
import { pipe } from "@effect/data/Function";
111113
import * as Effect from "@effect/io/Effect";
112-
import * as S from "@effect/schema/Schema";
114+
import * as Schema from "@effect/schema/Schema";
113115

114-
const responseSchema = S.struct({ name: S.string });
116+
const responseSchema = Schema.struct({ name: Schema.string });
115117

116118
const api = pipe(
117119
Http.api(),
@@ -134,25 +136,80 @@ Each endpoint can declare its inputs. Inputs can be passed as
134136
- `body` - request body
135137
- `query` - query parameters
136138
- `params` - path parameters
139+
- `headers` - request headers
137140

138-
Inputs are specified as part of the schemas structure.
141+
Inputs are specified as part of the schemas structure.
139142

140143
#### Example
141144

142145
```typescript
143-
import * as Http from "../src";
146+
import * as Http from "effect-http";
147+
148+
import * as Schema from "@effect/schema/Schema";
144149

145150
const api = pipe(
146151
Http.api({ title: "My api" }),
147152
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 },
152157
}),
153158
);
154159
```
155160

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+
156213
### Layers and services
157214

158215
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.
211268
- 400 `Http.invalidQueryError`
212269
- 400 `Http.invalidParamsError`
213270
- 400 `Http.invalidBodyError`
271+
- 400 `Http.invalidHeadersError`
214272
- 401 `Http.unauthorizedError`
215273
- 404 `Http.notFoundError`
216274
- 409 `Http.conflictError`
275+
- 429 `Http.tooManyRequestsError`
217276

218277
##### 5xx
219278

@@ -237,8 +296,8 @@ import * as S from "@effect/schema/Schema";
237296
const api = pipe(
238297
Http.api({ title: "Users API" }),
239298
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 }),
242301
}),
243302
);
244303
```
@@ -349,7 +408,7 @@ import { pipe } from "@effect/data/Function";
349408
import * as Effect from "@effect/io/Effect";
350409
import * as S from "@effect/schema/Schema";
351410
352-
const responseSchema = S.struct({ name: S.string });
411+
const responseSchema = Schema.struct({ name: Schema.string });
353412
354413
const testApi = pipe(
355414
Http.apiGroup("test"),
@@ -386,7 +445,7 @@ _(This is a complete standalone code example)_
386445
387446
The OpenApi UI groups endpoints using the specified groups.
388447
389-
![example-generated-open-api-ui](assets/exmple-server-open-api.png)
448+
![example-generated-open-api-ui](assets/example-server-openapi-ui.png)
390449
391450
## Cookbook
392451
@@ -409,8 +468,8 @@ import * as S from "@effect/schema/Schema";
409468
const api = pipe(
410469
Http.api(),
411470
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 }),
414473
}),
415474
);
416475

0 commit comments

Comments
 (0)