Skip to content

Commit 2b35561

Browse files
Migrate io-ts-http documentation to Docusaurus website following Diátaxis framework
Update documentation to follow Microsoft Writing Style Guide chore: prettier Remove io-ts-http-documentation-migration-plan.md
1 parent bcda594 commit 2b35561

File tree

10 files changed

+1267
-337
lines changed

10 files changed

+1267
-337
lines changed

Diff for: packages/io-ts-http/README.md

+38-337
Large diffs are not rendered by default.

Diff for: website/docs/explanation/io-ts-http-concepts.md

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
sidebar_position: 2
3+
---
4+
5+
# Understanding io-ts-http
6+
7+
## Introduction
8+
9+
Use `io-ts-http` as the definition language for api-ts specifications. It helps you
10+
define precise API contracts for web servers. This article explains the key concepts and
11+
design principles behind `io-ts-http`.
12+
13+
## Core concepts
14+
15+
### API contract as a type system
16+
17+
Express your API contract through TypeScript's type system with runtime validation using
18+
`io-ts`. This approach offers three main benefits:
19+
20+
1. **Type safety**: Use the same types for both clients and servers to ensure
21+
compatibility.
22+
2. **Runtime validation**: Validate data at runtime, unlike TypeScript's types which are
23+
erased during compilation.
24+
3. **Parse, don't validate**: Not only validate input but also parse it into the correct
25+
shape and types.
26+
27+
### Building blocks
28+
29+
`io-ts-http` has three main components:
30+
31+
1. **`apiSpec`**: Create the top-level definition of your API and organize routes under
32+
operation names.
33+
2. **`httpRoute`**: Define individual routes with their paths, methods, request schemas,
34+
and response schemas.
35+
3. **`httpRequest`**: Specify the expected shape of HTTP requests, including parameters,
36+
query strings, headers, and bodies.
37+
38+
## How it works
39+
40+
When you define an API with `io-ts-http`, you create a specification that works in
41+
multiple ways:
42+
43+
1. **Server-side**: Validate and parse incoming requests to ensure they match your API
44+
contract.
45+
2. **Client-side**: Ensure requests are properly formed before sending them to the
46+
server.
47+
3. **Documentation**: Generate API documentation such as OpenAPI specifications.
48+
49+
The same specification works everywhere, eliminating discrepancies between your
50+
implementation and documentation.
51+
52+
### Property flattening
53+
54+
Property flattening is an important concept in `io-ts-http`. When you decode HTTP
55+
requests, the system flattens properties from different sources (path parameters, query
56+
parameters, headers, and body) into a single object.
57+
58+
For example, if you have:
59+
60+
- A path parameter `id`
61+
- A query parameter `filter`
62+
- A body with properties `name` and `email`
63+
64+
The decoded object becomes:
65+
66+
```typescript
67+
{
68+
id: string,
69+
filter: string,
70+
name: string,
71+
email: string
72+
}
73+
```
74+
75+
This makes it easier to work with the data without accessing different parts of the
76+
request separately.
77+
78+
## Design principles
79+
80+
### 1. Type safety first
81+
82+
`io-ts-http` prioritizes type safety. It uses `io-ts` codecs to ensure your API
83+
contracts are type-checked at compile time and validated at runtime.
84+
85+
### 2. Separation of concerns
86+
87+
The library separates:
88+
89+
- Defining what your API looks like (`apiSpec` and `httpRoute`)
90+
- Defining how requests and responses should be structured (`httpRequest`)
91+
- Processing HTTP requests and responses (handled by other packages)
92+
93+
### 3. Flexibility
94+
95+
`io-ts-http` is flexible. It doesn't dictate how to implement your server or client—only
96+
how to define your API contract. You can use it with any HTTP framework or client
97+
library.
98+
99+
## When to use io-ts-http
100+
101+
Use `io-ts-http` when you need to:
102+
103+
1. Build a TypeScript API with type safety between client and server
104+
2. Validate complex request structures with nested objects, arrays, or optional fields
105+
3. Generate documentation from your code to keep it current
106+
4. Implement the "Parse, Don't Validate" pattern for robust error handling
107+
108+
## Conclusion
109+
110+
`io-ts-http` offers a powerful way to define type-safe API contracts for your entire
111+
application stack. By centralizing your API definition in a single source of truth, you
112+
can ensure consistency and reduce errors in your application.

Diff for: website/docs/how-to-guides/advanced-http-routes.md

+234
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Advanced HTTP route patterns
2+
3+
Learn advanced patterns for defining HTTP routes with `io-ts-http` beyond the basic
4+
usage examples.
5+
6+
## Work with non-object body types
7+
8+
By default, `httpRequest` assumes the request body is a JSON object. Sometimes you need
9+
to accept other types like strings, numbers, or arrays.
10+
11+
### Accept a string body
12+
13+
Use `t.intersection` to combine `httpRequest` with a custom type that accepts a string
14+
body:
15+
16+
```typescript
17+
import * as t from 'io-ts';
18+
import { httpRoute, httpRequest } from '@api-ts/io-ts-http';
19+
20+
const StringBodyRoute = httpRoute({
21+
path: '/example/{id}',
22+
method: 'POST',
23+
request: t.intersection([
24+
httpRequest({
25+
params: { id: t.string },
26+
}),
27+
t.type({
28+
body: t.string,
29+
}),
30+
]),
31+
response: {
32+
200: t.string,
33+
},
34+
});
35+
36+
// Decoded type
37+
type DecodedType = {
38+
id: string;
39+
body: string;
40+
};
41+
```
42+
43+
This approach breaks the abstraction slightly by exposing a `body` property in the
44+
decoded type, but it works effectively.
45+
46+
### Accept an array body
47+
48+
Similarly, accept an array body:
49+
50+
```typescript
51+
const ArrayBodyRoute = httpRoute({
52+
path: '/batch-process',
53+
method: 'POST',
54+
request: t.intersection([
55+
httpRequest({}),
56+
t.type({
57+
body: t.array(t.string),
58+
}),
59+
]),
60+
response: {
61+
200: t.type({
62+
processed: t.number,
63+
}),
64+
},
65+
});
66+
67+
// Decoded type
68+
type DecodedType = {
69+
body: string[];
70+
};
71+
```
72+
73+
## Create conditional request parameters
74+
75+
Sometimes you need parameters that are required only when other parameters have specific
76+
values.
77+
78+
### Use union types
79+
80+
Use `t.union` with multiple `httpRequest` objects to handle conditional parameters:
81+
82+
```typescript
83+
const SearchRoute = httpRoute({
84+
path: '/search',
85+
method: 'GET',
86+
request: t.union([
87+
// When searching by keyword
88+
httpRequest({
89+
query: {
90+
type: t.literal('keyword'),
91+
keyword: t.string,
92+
},
93+
}),
94+
// When searching by category
95+
httpRequest({
96+
query: {
97+
type: t.literal('category'),
98+
categoryId: NumberFromString,
99+
},
100+
}),
101+
// When searching by both
102+
httpRequest({
103+
query: {
104+
type: t.literal('combined'),
105+
keyword: t.string,
106+
categoryId: NumberFromString,
107+
},
108+
}),
109+
]),
110+
response: {
111+
200: t.array(
112+
t.type({
113+
id: t.string,
114+
title: t.string,
115+
}),
116+
),
117+
},
118+
});
119+
120+
// Decoded type will be a union:
121+
type DecodedType =
122+
| { type: 'keyword'; keyword: string }
123+
| { type: 'category'; categoryId: number }
124+
| { type: 'combined'; keyword: string; categoryId: number };
125+
```
126+
127+
## Add optional headers
128+
129+
HTTP headers are often optional. Use the `optional` combinator to define optional
130+
headers:
131+
132+
```typescript
133+
import { httpRequest, optional } from '@api-ts/io-ts-http';
134+
135+
const RequestWithOptionalHeaders = httpRoute({
136+
path: '/resource',
137+
method: 'GET',
138+
request: httpRequest({
139+
headers: {
140+
authorization: t.string,
141+
'cache-control': optional(t.string),
142+
'if-modified-since': optional(t.string),
143+
},
144+
}),
145+
response: {
146+
200: t.object,
147+
},
148+
});
149+
150+
// Decoded type
151+
type DecodedType = {
152+
authorization: string;
153+
'cache-control'?: string;
154+
'if-modified-since'?: string;
155+
};
156+
```
157+
158+
## Handle file uploads
159+
160+
File uploads typically use `multipart/form-data` encoding. While `io-ts-http` doesn't
161+
directly support file uploads, you can treat the file as an opaque object in the type
162+
system and handle the file processing separately:
163+
164+
```typescript
165+
const FileUploadRoute = httpRoute({
166+
path: '/upload',
167+
method: 'POST',
168+
request: httpRequest({
169+
body: {
170+
// In the type system, just indicate a file is expected
171+
// Your server framework will handle the actual file
172+
file: t.unknown,
173+
description: optional(t.string),
174+
},
175+
}),
176+
response: {
177+
200: t.type({
178+
fileId: t.string,
179+
size: t.number,
180+
}),
181+
},
182+
});
183+
```
184+
185+
## Combine multiple request sources
186+
187+
Sometimes you need to extract information from multiple sources, such as getting an ID
188+
from the path, authentication from headers, and data from the body:
189+
190+
```typescript
191+
const ComplexRoute = httpRoute({
192+
path: '/users/{userId}/profile',
193+
method: 'PUT',
194+
request: httpRequest({
195+
params: {
196+
userId: NumberFromString,
197+
},
198+
headers: {
199+
authorization: t.string,
200+
},
201+
body: {
202+
name: t.string,
203+
email: t.string,
204+
preferences: t.type({
205+
theme: t.union([t.literal('light'), t.literal('dark')]),
206+
notifications: t.boolean,
207+
}),
208+
},
209+
}),
210+
response: {
211+
200: t.type({
212+
success: t.boolean,
213+
}),
214+
},
215+
});
216+
217+
// Decoded type
218+
type DecodedType = {
219+
userId: number;
220+
authorization: string;
221+
name: string;
222+
email: string;
223+
preferences: {
224+
theme: 'light' | 'dark';
225+
notifications: boolean;
226+
};
227+
};
228+
```
229+
230+
## Summary
231+
232+
These advanced patterns help you define complex HTTP routes that accurately reflect your
233+
API's requirements. By combining `io-ts` with `httpRequest` and `httpRoute`, you can
234+
create type-safe APIs with sophisticated validation logic.

0 commit comments

Comments
 (0)