@afoures/http-client 0.1.1 → 0.3.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.
- package/README.md +5 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/lib/endpoint.d.mts +2 -2
- package/dist/lib/endpoint.mjs +118 -67
- package/dist/lib/errors.d.mts +59 -35
- package/dist/lib/errors.mjs +25 -27
- package/dist/lib/http-client.d.mts +21 -21
- package/dist/lib/http-client.mjs +133 -13
- package/dist/lib/types.d.mts +14 -46
- package/docs/endpoint-definition.md +1 -1
- package/docs/error-handling.md +3 -3
- package/docs/http-client.md +5 -5
- package/docs/response-parsing.md +14 -10
- package/docs/retry-policy.md +1 -1
- package/docs/schema-integration.md +14 -5
- package/docs/serialization.md +13 -12
- package/package.json +5 -5
package/docs/response-parsing.md
CHANGED
|
@@ -56,7 +56,9 @@ type ServerErrorResponse<Error> = {
|
|
|
56
56
|
|
|
57
57
|
Define a `data` parser for successful responses:
|
|
58
58
|
|
|
59
|
-
### JSON
|
|
59
|
+
### JSON
|
|
60
|
+
|
|
61
|
+
Use `parse: 'json'` to parse the response body as JSON:
|
|
60
62
|
|
|
61
63
|
```typescript
|
|
62
64
|
const endpoint = new Endpoint({
|
|
@@ -67,6 +69,7 @@ const endpoint = new Endpoint({
|
|
|
67
69
|
id: z.string(),
|
|
68
70
|
name: z.string(),
|
|
69
71
|
}),
|
|
72
|
+
parse: 'json',
|
|
70
73
|
},
|
|
71
74
|
})
|
|
72
75
|
|
|
@@ -84,7 +87,7 @@ const endpoint = new Endpoint({
|
|
|
84
87
|
pathname: '/health',
|
|
85
88
|
data: {
|
|
86
89
|
schema: z.string(),
|
|
87
|
-
|
|
90
|
+
parse: 'text',
|
|
88
91
|
},
|
|
89
92
|
})
|
|
90
93
|
```
|
|
@@ -97,7 +100,7 @@ const endpoint = new Endpoint({
|
|
|
97
100
|
pathname: '/data',
|
|
98
101
|
data: {
|
|
99
102
|
schema: z.object({ value: z.number() }),
|
|
100
|
-
|
|
103
|
+
parse: async (body) => {
|
|
101
104
|
const text = await new Response(body).text()
|
|
102
105
|
return JSON.parse(text)
|
|
103
106
|
},
|
|
@@ -129,13 +132,13 @@ Define an `error` parser for error responses:
|
|
|
129
132
|
const endpoint = new Endpoint({
|
|
130
133
|
method: 'POST',
|
|
131
134
|
pathname: '/users',
|
|
132
|
-
body: { schema: z.object({ name: z.string() }) },
|
|
135
|
+
body: { schema: z.object({ name: z.string() }), serialize: 'json' },
|
|
133
136
|
error: {
|
|
134
137
|
schema: z.object({
|
|
135
138
|
message: z.string(),
|
|
136
139
|
code: z.string(),
|
|
137
140
|
}),
|
|
138
|
-
|
|
141
|
+
parse: 'json',
|
|
139
142
|
},
|
|
140
143
|
})
|
|
141
144
|
|
|
@@ -154,7 +157,7 @@ const endpoint = new Endpoint({
|
|
|
154
157
|
pathname: '/users/(:id)',
|
|
155
158
|
error: {
|
|
156
159
|
schema: z.string(),
|
|
157
|
-
|
|
160
|
+
parse: 'text',
|
|
158
161
|
},
|
|
159
162
|
})
|
|
160
163
|
|
|
@@ -193,6 +196,7 @@ const endpoint = new Endpoint({
|
|
|
193
196
|
name: z.string().transform(s => s.toUpperCase()),
|
|
194
197
|
createdAt: z.string().transform(s => new Date(s)),
|
|
195
198
|
}),
|
|
199
|
+
parse: 'json',
|
|
196
200
|
},
|
|
197
201
|
})
|
|
198
202
|
|
|
@@ -203,15 +207,15 @@ if (result.ok) {
|
|
|
203
207
|
}
|
|
204
208
|
```
|
|
205
209
|
|
|
206
|
-
##
|
|
210
|
+
## Parse Errors
|
|
207
211
|
|
|
208
|
-
If response parsing fails validation, a `
|
|
212
|
+
If response parsing fails validation, a `ParseError` is returned:
|
|
209
213
|
|
|
210
214
|
```typescript
|
|
211
215
|
const result = await endpoint.parse_response(response)
|
|
212
216
|
|
|
213
|
-
if (result instanceof
|
|
214
|
-
console.log(result.message) // "Response
|
|
217
|
+
if (result instanceof ParseError) {
|
|
218
|
+
console.log(result.message) // "Response parsing failed"
|
|
215
219
|
console.log(result.cause) // Schema validation issues
|
|
216
220
|
}
|
|
217
221
|
```
|
package/docs/retry-policy.md
CHANGED
|
@@ -15,6 +15,7 @@ const endpoint = new Endpoint({
|
|
|
15
15
|
name: z.string().min(1),
|
|
16
16
|
email: z.string().email(),
|
|
17
17
|
}),
|
|
18
|
+
serialize: 'json',
|
|
18
19
|
},
|
|
19
20
|
data: {
|
|
20
21
|
schema: z.object({
|
|
@@ -22,6 +23,7 @@ const endpoint = new Endpoint({
|
|
|
22
23
|
name: z.string(),
|
|
23
24
|
createdAt: z.string().datetime(),
|
|
24
25
|
}),
|
|
26
|
+
parse: 'json',
|
|
25
27
|
},
|
|
26
28
|
})
|
|
27
29
|
```
|
|
@@ -43,6 +45,7 @@ const endpoint = new Endpoint({
|
|
|
43
45
|
schema: z.object({
|
|
44
46
|
createdAt: z.string().transform(s => new Date(s)), // parse ISO to Date
|
|
45
47
|
}),
|
|
48
|
+
parse: 'json',
|
|
46
49
|
},
|
|
47
50
|
})
|
|
48
51
|
|
|
@@ -66,6 +69,7 @@ const endpoint = new Endpoint({
|
|
|
66
69
|
email: 'string',
|
|
67
70
|
age: 'number?',
|
|
68
71
|
}),
|
|
72
|
+
serialize: 'json',
|
|
69
73
|
},
|
|
70
74
|
data: {
|
|
71
75
|
schema: type({
|
|
@@ -73,6 +77,7 @@ const endpoint = new Endpoint({
|
|
|
73
77
|
name: 'string',
|
|
74
78
|
'email?': 'string',
|
|
75
79
|
}),
|
|
80
|
+
parse: 'json',
|
|
76
81
|
},
|
|
77
82
|
})
|
|
78
83
|
```
|
|
@@ -90,6 +95,7 @@ const endpoint = new Endpoint({
|
|
|
90
95
|
id: 'string',
|
|
91
96
|
'createdAt': 'string.parse(v => new Date(v))',
|
|
92
97
|
}),
|
|
98
|
+
parse: 'json',
|
|
93
99
|
},
|
|
94
100
|
})
|
|
95
101
|
```
|
|
@@ -107,12 +113,14 @@ const endpoint = new Endpoint({
|
|
|
107
113
|
name: v.pipe(v.string(), v.minLength(1)),
|
|
108
114
|
email: v.pipe(v.string(), v.email()),
|
|
109
115
|
}),
|
|
116
|
+
serialize: 'json',
|
|
110
117
|
},
|
|
111
118
|
data: {
|
|
112
119
|
schema: v.object({
|
|
113
120
|
id: v.string(),
|
|
114
121
|
name: v.string(),
|
|
115
122
|
}),
|
|
123
|
+
parse: 'json',
|
|
116
124
|
},
|
|
117
125
|
})
|
|
118
126
|
```
|
|
@@ -130,6 +138,7 @@ const endpoint = new Endpoint({
|
|
|
130
138
|
id: v.string(),
|
|
131
139
|
createdAt: v.pipe(v.string(), v.transform(s => new Date(s))),
|
|
132
140
|
}),
|
|
141
|
+
parse: 'json',
|
|
133
142
|
},
|
|
134
143
|
})
|
|
135
144
|
```
|
|
@@ -180,24 +189,24 @@ const UserSchema = z.object({
|
|
|
180
189
|
const CreateUserSchema = UserSchema.omit({ id: true })
|
|
181
190
|
|
|
182
191
|
const api = http_client({
|
|
183
|
-
|
|
192
|
+
base_url: 'https://api.example.com',
|
|
184
193
|
endpoints: {
|
|
185
194
|
users: {
|
|
186
195
|
list: new Endpoint({
|
|
187
196
|
method: 'GET',
|
|
188
197
|
pathname: '/users',
|
|
189
|
-
data: { schema: z.array(UserSchema) },
|
|
198
|
+
data: { schema: z.array(UserSchema), parse: 'json' },
|
|
190
199
|
}),
|
|
191
200
|
get: new Endpoint({
|
|
192
201
|
method: 'GET',
|
|
193
202
|
pathname: '/users/(:id)',
|
|
194
|
-
data: { schema: UserSchema },
|
|
203
|
+
data: { schema: UserSchema, parse: 'json' },
|
|
195
204
|
}),
|
|
196
205
|
create: new Endpoint({
|
|
197
206
|
method: 'POST',
|
|
198
207
|
pathname: '/users',
|
|
199
|
-
body: { schema: CreateUserSchema },
|
|
200
|
-
data: { schema: UserSchema },
|
|
208
|
+
body: { schema: CreateUserSchema, serialize: 'json' },
|
|
209
|
+
data: { schema: UserSchema, parse: 'json' },
|
|
201
210
|
}),
|
|
202
211
|
},
|
|
203
212
|
},
|
package/docs/serialization.md
CHANGED
|
@@ -17,7 +17,7 @@ const endpoint = new Endpoint({
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
const url = await endpoint.generate_url({
|
|
20
|
-
|
|
20
|
+
base_url: 'https://api.example.com',
|
|
21
21
|
params: { id: '123' },
|
|
22
22
|
})
|
|
23
23
|
// https://api.example.com/users/123
|
|
@@ -41,7 +41,7 @@ const endpoint = new Endpoint({
|
|
|
41
41
|
|
|
42
42
|
### Custom Serialization
|
|
43
43
|
|
|
44
|
-
Provide a `
|
|
44
|
+
Provide a `serialize` function to transform validated params:
|
|
45
45
|
|
|
46
46
|
```typescript
|
|
47
47
|
const endpoint = new Endpoint({
|
|
@@ -49,12 +49,12 @@ const endpoint = new Endpoint({
|
|
|
49
49
|
pathname: '/users/(:id)',
|
|
50
50
|
params: {
|
|
51
51
|
schema: z.object({ id: z.number() }),
|
|
52
|
-
|
|
52
|
+
serialize: (data) => ({ id: `user-${data.id}` }),
|
|
53
53
|
},
|
|
54
54
|
})
|
|
55
55
|
|
|
56
56
|
const url = await endpoint.generate_url({
|
|
57
|
-
|
|
57
|
+
base_url: 'https://api.example.com',
|
|
58
58
|
params: { id: 123 },
|
|
59
59
|
})
|
|
60
60
|
// https://api.example.com/users/user-123
|
|
@@ -79,7 +79,7 @@ const endpoint = new Endpoint({
|
|
|
79
79
|
})
|
|
80
80
|
|
|
81
81
|
const url = await endpoint.generate_url({
|
|
82
|
-
|
|
82
|
+
base_url: 'https://api.example.com',
|
|
83
83
|
query: { page: 1, search: 'john' },
|
|
84
84
|
})
|
|
85
85
|
// https://api.example.com/users?page=1&search=john
|
|
@@ -95,7 +95,7 @@ const endpoint = new Endpoint({
|
|
|
95
95
|
schema: z.object({
|
|
96
96
|
tags: z.array(z.string()),
|
|
97
97
|
}),
|
|
98
|
-
|
|
98
|
+
serialize: (data) => {
|
|
99
99
|
const params = new URLSearchParams()
|
|
100
100
|
params.set('tags', data.tags.join(','))
|
|
101
101
|
return params
|
|
@@ -104,7 +104,7 @@ const endpoint = new Endpoint({
|
|
|
104
104
|
})
|
|
105
105
|
|
|
106
106
|
const url = await endpoint.generate_url({
|
|
107
|
-
|
|
107
|
+
base_url: 'https://api.example.com',
|
|
108
108
|
query: { tags: ['admin', 'active'] },
|
|
109
109
|
})
|
|
110
110
|
// https://api.example.com/users?tags=admin,active
|
|
@@ -114,9 +114,9 @@ const url = await endpoint.generate_url({
|
|
|
114
114
|
|
|
115
115
|
Request bodies are serialized for POST, PUT, PATCH, and DELETE methods.
|
|
116
116
|
|
|
117
|
-
### JSON
|
|
117
|
+
### JSON
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
Use `serialize: 'json'` to serialize the body as JSON:
|
|
120
120
|
|
|
121
121
|
```typescript
|
|
122
122
|
const endpoint = new Endpoint({
|
|
@@ -127,6 +127,7 @@ const endpoint = new Endpoint({
|
|
|
127
127
|
name: z.string(),
|
|
128
128
|
email: z.string().email(),
|
|
129
129
|
}),
|
|
130
|
+
serialize: 'json',
|
|
130
131
|
},
|
|
131
132
|
})
|
|
132
133
|
|
|
@@ -150,7 +151,7 @@ const endpoint = new Endpoint({
|
|
|
150
151
|
file: z.instanceof(File),
|
|
151
152
|
name: z.string(),
|
|
152
153
|
}),
|
|
153
|
-
|
|
154
|
+
serialize: (data) => {
|
|
154
155
|
const formData = new FormData()
|
|
155
156
|
formData.append('file', data.file)
|
|
156
157
|
formData.append('name', data.name)
|
|
@@ -171,7 +172,7 @@ const endpoint = new Endpoint({
|
|
|
171
172
|
username: z.string(),
|
|
172
173
|
password: z.string(),
|
|
173
174
|
}),
|
|
174
|
-
|
|
175
|
+
serialize: (data) => {
|
|
175
176
|
const params = new URLSearchParams()
|
|
176
177
|
params.set('username', data.username)
|
|
177
178
|
params.set('password', data.password)
|
|
@@ -189,7 +190,7 @@ const endpoint = new Endpoint({
|
|
|
189
190
|
pathname: '/echo',
|
|
190
191
|
body: {
|
|
191
192
|
schema: z.string(),
|
|
192
|
-
|
|
193
|
+
serialize: (text) => ({
|
|
193
194
|
body: text,
|
|
194
195
|
content_type: 'text/plain',
|
|
195
196
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@afoures/http-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A typesafe and robust HTTP client",
|
|
5
5
|
"homepage": "https://github.com/afoures/http-client#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"@standard-schema/spec": "^1.1.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@afoures/auto-release": "^0.
|
|
38
|
+
"@afoures/auto-release": "^0.5.0",
|
|
39
39
|
"@arktype/attest": "^0.56.0",
|
|
40
|
-
"@types/node": "^24.12.
|
|
41
|
-
"msw": "^2.
|
|
40
|
+
"@types/node": "^24.12.2",
|
|
41
|
+
"msw": "^2.13.2",
|
|
42
42
|
"oxfmt": "^0.36.0",
|
|
43
|
-
"oxlint": "^1.
|
|
43
|
+
"oxlint": "^1.59.0",
|
|
44
44
|
"tsdown": "^0.21.0",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"zod": "^4.3.6"
|