@decaf-ts/for-http 0.2.3 → 0.2.5
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 +330 -19
- package/dist/for-http.cjs +35 -23
- package/dist/for-http.esm.cjs +35 -23
- package/lib/RestRepository.cjs +2 -2
- package/lib/RestRepository.d.ts +2 -2
- package/lib/RestService.cjs +25 -2
- package/lib/RestService.d.ts +25 -2
- package/lib/adapter.cjs +9 -20
- package/lib/adapter.d.ts +5 -14
- package/lib/axios/axios.cjs +12 -14
- package/lib/axios/axios.d.ts +3 -8
- package/lib/esm/RestRepository.d.ts +2 -2
- package/lib/esm/RestRepository.js +2 -2
- package/lib/esm/RestService.d.ts +25 -2
- package/lib/esm/RestService.js +25 -2
- package/lib/esm/adapter.d.ts +5 -14
- package/lib/esm/adapter.js +9 -20
- package/lib/esm/axios/axios.d.ts +3 -8
- package/lib/esm/axios/axios.js +12 -14
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/index.cjs +1 -1
- package/lib/index.d.ts +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## decaf's Http Module
|
|
4
4
|
|
|
5
|
+
A TypeScript library for seamless REST API interactions. This module provides a flexible and type-safe way to communicate with HTTP-based services using the repository pattern. It includes adapters for different HTTP clients (with Axios implementation provided), repository and service classes for CRUD operations, and comprehensive type definitions to ensure type safety throughout your API interactions.
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|

|
|
@@ -18,12 +19,6 @@
|
|
|
18
19
|

|
|
19
20
|

|
|
20
21
|
|
|
21
|
-

|
|
22
|
-

|
|
23
|
-

|
|
24
|
-

|
|
25
|
-
|
|
26
|
-
|
|
27
22
|

|
|
28
23
|

|
|
29
24
|

|
|
@@ -35,27 +30,343 @@ Documentation available [here](https://decaf-ts.github.io/for-http/)
|
|
|
35
30
|
|
|
36
31
|
### Description
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
The `@decaf-ts/for-http` library provides a robust and type-safe solution for interacting with REST APIs in TypeScript applications. Built on top of the core Decaf framework, it implements the repository pattern to offer a clean and consistent interface for HTTP operations.
|
|
34
|
+
|
|
35
|
+
#### Architecture
|
|
36
|
+
|
|
37
|
+
The library is structured around several key components:
|
|
38
|
+
|
|
39
|
+
1. **HTTP Adapter**: The `HttpAdapter` class serves as the foundation, providing an abstract interface for HTTP operations. It handles URL construction, error parsing, and implements the adapter pattern to work with different HTTP clients.
|
|
40
|
+
|
|
41
|
+
2. **Axios Implementation**: The library includes a concrete implementation of the HTTP adapter using Axios (`AxiosHttpAdapter`), demonstrating how to integrate with popular HTTP clients.
|
|
42
|
+
|
|
43
|
+
3. **Repository Pattern**: The `RestRepository` class extends the core Repository class to provide a high-level interface for CRUD operations on REST resources. It works with model classes and handles the mapping between your domain models and API endpoints.
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
4. **Service Layer**: The `RestService` class offers both individual and bulk CRUD operations, with support for the observer pattern to notify subscribers of changes.
|
|
41
46
|
|
|
47
|
+
5. **Type Definitions**: Comprehensive type definitions ensure type safety throughout your API interactions, with interfaces like `HttpFlags` and `HttpConfig` providing configuration options.
|
|
48
|
+
|
|
49
|
+
#### Key Features
|
|
50
|
+
|
|
51
|
+
- **Type Safety**: Leverages TypeScript's type system to ensure API interactions are type-safe
|
|
52
|
+
- **Repository Pattern**: Implements the repository pattern for clean separation of concerns
|
|
53
|
+
- **CRUD Operations**: Provides standard create, read, update, and delete operations
|
|
54
|
+
- **Bulk Operations**: Supports bulk operations for efficient handling of multiple resources
|
|
55
|
+
- **Extensibility**: Designed to be extended with different HTTP client implementations
|
|
56
|
+
- **Error Handling**: Includes robust error handling and parsing
|
|
57
|
+
- **Observer Pattern**: Implements the observer pattern for reactive programming
|
|
58
|
+
|
|
59
|
+
This library is ideal for applications that need to interact with REST APIs in a structured, type-safe manner, particularly those already using the Decaf framework.
|
|
42
60
|
|
|
43
61
|
|
|
44
62
|
### How to Use
|
|
45
63
|
|
|
46
|
-
- [Initial Setup](./tutorials/For%20Developers.md#_initial-setup_)
|
|
47
|
-
- [Installation](./tutorials/For%20Developers.md#installation)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
- [Initial Setup](./workdocs/tutorials/For%20Developers.md#_initial-setup_)
|
|
65
|
+
- [Installation](./workdocs/tutorials/For%20Developers.md#installation)
|
|
66
|
+
|
|
67
|
+
## Basic Usage
|
|
68
|
+
|
|
69
|
+
### Setting Up an HTTP Adapter with Axios
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import axios from 'axios';
|
|
73
|
+
import { AxiosHttpAdapter } from '@decaf-ts/for-http';
|
|
74
|
+
|
|
75
|
+
// Create an HTTP configuration
|
|
76
|
+
const config = {
|
|
77
|
+
protocol: 'https',
|
|
78
|
+
host: 'api.example.com'
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Create an Axios HTTP adapter
|
|
82
|
+
const httpAdapter = new AxiosHttpAdapter(axios.create(), config);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Creating a Model Class
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { Model } from '@decaf-ts/decorator-validation';
|
|
89
|
+
|
|
90
|
+
class User extends Model {
|
|
91
|
+
id: string;
|
|
92
|
+
name: string;
|
|
93
|
+
email: string;
|
|
94
|
+
|
|
95
|
+
constructor(data?: Partial<User>) {
|
|
96
|
+
super();
|
|
97
|
+
Object.assign(this, data);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Using RestRepository for CRUD Operations
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { RestRepository } from '@decaf-ts/for-http';
|
|
106
|
+
|
|
107
|
+
// Create a repository for the User model
|
|
108
|
+
const userRepository = new RestRepository(httpAdapter, User);
|
|
109
|
+
|
|
110
|
+
// Create a new user
|
|
111
|
+
const newUser = new User({
|
|
112
|
+
name: 'John Doe',
|
|
113
|
+
email: 'john@example.com'
|
|
114
|
+
});
|
|
115
|
+
const createdUser = await userRepository.create(newUser);
|
|
116
|
+
|
|
117
|
+
// Read a user by ID
|
|
118
|
+
const user = await userRepository.findById('123');
|
|
119
|
+
|
|
120
|
+
// Update a user
|
|
121
|
+
user.name = 'Jane Doe';
|
|
122
|
+
await userRepository.update(user);
|
|
123
|
+
|
|
124
|
+
// Delete a user
|
|
125
|
+
await userRepository.delete('123');
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Using RestService for Advanced Operations
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { RestService } from '@decaf-ts/for-http';
|
|
57
132
|
|
|
133
|
+
// Create a service for the User model
|
|
134
|
+
const userService = new RestService(httpAdapter, User);
|
|
58
135
|
|
|
136
|
+
// Create a new user
|
|
137
|
+
const newUser = new User({
|
|
138
|
+
name: 'John Doe',
|
|
139
|
+
email: 'john@example.com'
|
|
140
|
+
});
|
|
141
|
+
const createdUser = await userService.create(newUser);
|
|
142
|
+
|
|
143
|
+
// Read a user by ID
|
|
144
|
+
const user = await userService.read('123');
|
|
145
|
+
|
|
146
|
+
// Update a user
|
|
147
|
+
user.name = 'Jane Doe';
|
|
148
|
+
await userService.update(user);
|
|
149
|
+
|
|
150
|
+
// Delete a user
|
|
151
|
+
await userService.delete('123');
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Bulk Operations with RestService
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { RestService } from '@decaf-ts/for-http';
|
|
158
|
+
|
|
159
|
+
// Create a service for the User model
|
|
160
|
+
const userService = new RestService(httpAdapter, User);
|
|
161
|
+
|
|
162
|
+
// Create multiple users
|
|
163
|
+
const users = [
|
|
164
|
+
new User({ name: 'John Doe', email: 'john@example.com' }),
|
|
165
|
+
new User({ name: 'Jane Doe', email: 'jane@example.com' })
|
|
166
|
+
];
|
|
167
|
+
const createdUsers = await userService.createAll(users);
|
|
168
|
+
|
|
169
|
+
// Read multiple users by ID
|
|
170
|
+
const userIds = ['123', '456'];
|
|
171
|
+
const fetchedUsers = await userService.readAll(userIds);
|
|
172
|
+
|
|
173
|
+
// Update multiple users
|
|
174
|
+
const usersToUpdate = [
|
|
175
|
+
new User({ id: '123', name: 'John Smith' }),
|
|
176
|
+
new User({ id: '456', name: 'Jane Smith' })
|
|
177
|
+
];
|
|
178
|
+
const updatedUsers = await userService.updateAll(usersToUpdate);
|
|
179
|
+
|
|
180
|
+
// Delete multiple users
|
|
181
|
+
await userService.deleteAll(['123', '456']);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Using the Observer Pattern
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { RestService } from '@decaf-ts/for-http';
|
|
188
|
+
import { Observer } from '@decaf-ts/core';
|
|
189
|
+
|
|
190
|
+
// Create a service for the User model
|
|
191
|
+
const userService = new RestService(httpAdapter, User);
|
|
192
|
+
|
|
193
|
+
// Create an observer
|
|
194
|
+
const userObserver: Observer<User> = {
|
|
195
|
+
update: (user) => {
|
|
196
|
+
console.log('User updated:', user);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Register the observer
|
|
201
|
+
userService.observe(userObserver);
|
|
202
|
+
|
|
203
|
+
// When operations are performed, observers will be notified
|
|
204
|
+
await userService.create(new User({ name: 'John Doe' }));
|
|
205
|
+
|
|
206
|
+
// Unregister the observer when done
|
|
207
|
+
userService.unObserve(userObserver);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Custom HTTP Adapter Implementation
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { HttpAdapter } from '@decaf-ts/for-http';
|
|
214
|
+
import { HttpConfig, HttpFlags } from '@decaf-ts/for-http';
|
|
215
|
+
import { Context } from '@decaf-ts/db-decorators';
|
|
216
|
+
import SomeHttpClient from 'some-http-client';
|
|
217
|
+
|
|
218
|
+
// Create a custom HTTP adapter for a different HTTP client
|
|
219
|
+
class CustomHttpAdapter extends HttpAdapter<
|
|
220
|
+
SomeHttpClient,
|
|
221
|
+
any,
|
|
222
|
+
HttpFlags,
|
|
223
|
+
Context<HttpFlags>
|
|
224
|
+
> {
|
|
225
|
+
constructor(native: SomeHttpClient, config: HttpConfig, alias?: string) {
|
|
226
|
+
super(native, config, 'custom', alias);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async request<V>(details: any): Promise<V> {
|
|
230
|
+
return this.native.sendRequest(details);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async create(tableName: string, id: string | number, model: Record<string, any>): Promise<Record<string, any>> {
|
|
234
|
+
try {
|
|
235
|
+
const url = this.url(tableName);
|
|
236
|
+
return this.native.post(url, model);
|
|
237
|
+
} catch (e: any) {
|
|
238
|
+
throw this.parseError(e);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async read(tableName: string, id: string | number | bigint): Promise<Record<string, any>> {
|
|
243
|
+
try {
|
|
244
|
+
const url = this.url(tableName, { id: id as string | number });
|
|
245
|
+
return this.native.get(url);
|
|
246
|
+
} catch (e: any) {
|
|
247
|
+
throw this.parseError(e);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async update(tableName: string, id: string | number, model: Record<string, any>): Promise<Record<string, any>> {
|
|
252
|
+
try {
|
|
253
|
+
const url = this.url(tableName);
|
|
254
|
+
return this.native.put(url, model);
|
|
255
|
+
} catch (e: any) {
|
|
256
|
+
throw this.parseError(e);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async delete(tableName: string, id: string | number | bigint): Promise<Record<string, any>> {
|
|
261
|
+
try {
|
|
262
|
+
const url = this.url(tableName, { id: id as string | number });
|
|
263
|
+
return this.native.delete(url);
|
|
264
|
+
} catch (e: any) {
|
|
265
|
+
throw this.parseError(e);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Using HTTP Flags for Request Configuration
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { HttpFlags } from '@decaf-ts/for-http';
|
|
275
|
+
|
|
276
|
+
// Create custom HTTP flags with headers
|
|
277
|
+
const flags: HttpFlags = {
|
|
278
|
+
headers: {
|
|
279
|
+
'Authorization': 'Bearer token123',
|
|
280
|
+
'Content-Type': 'application/json'
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Use flags with repository operations
|
|
285
|
+
const user = await userRepository.findById('123', { flags });
|
|
286
|
+
|
|
287
|
+
// Use flags with service operations
|
|
288
|
+
const createdUser = await userService.create(newUser, { flags });
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Complete Application Example
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import axios from 'axios';
|
|
295
|
+
import {
|
|
296
|
+
AxiosHttpAdapter,
|
|
297
|
+
RestRepository,
|
|
298
|
+
RestService,
|
|
299
|
+
HttpConfig
|
|
300
|
+
} from '@decaf-ts/for-http';
|
|
301
|
+
import { Model } from '@decaf-ts/decorator-validation';
|
|
302
|
+
|
|
303
|
+
// Define a model
|
|
304
|
+
class Product extends Model {
|
|
305
|
+
id: string;
|
|
306
|
+
name: string;
|
|
307
|
+
price: number;
|
|
308
|
+
|
|
309
|
+
constructor(data?: Partial<Product>) {
|
|
310
|
+
super();
|
|
311
|
+
Object.assign(this, data);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Configure the HTTP adapter
|
|
316
|
+
const config: HttpConfig = {
|
|
317
|
+
protocol: 'https',
|
|
318
|
+
host: 'api.mystore.com'
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Create the adapter
|
|
322
|
+
const adapter = new AxiosHttpAdapter(axios.create(), config);
|
|
323
|
+
|
|
324
|
+
// Create a repository
|
|
325
|
+
const productRepo = new RestRepository(adapter, Product);
|
|
326
|
+
|
|
327
|
+
// Create a service
|
|
328
|
+
const productService = new RestService(adapter, Product);
|
|
329
|
+
|
|
330
|
+
// Example application
|
|
331
|
+
async function manageProducts() {
|
|
332
|
+
try {
|
|
333
|
+
// Create a new product
|
|
334
|
+
const newProduct = new Product({
|
|
335
|
+
name: 'Smartphone',
|
|
336
|
+
price: 699.99
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const createdProduct = await productRepo.create(newProduct);
|
|
340
|
+
console.log('Created product:', createdProduct);
|
|
341
|
+
|
|
342
|
+
// Get all products
|
|
343
|
+
const products = await productRepo.findAll();
|
|
344
|
+
console.log('All products:', products);
|
|
345
|
+
|
|
346
|
+
// Update a product
|
|
347
|
+
createdProduct.price = 649.99;
|
|
348
|
+
const updatedProduct = await productService.update(createdProduct);
|
|
349
|
+
console.log('Updated product:', updatedProduct);
|
|
350
|
+
|
|
351
|
+
// Delete a product
|
|
352
|
+
await productService.delete(createdProduct.id);
|
|
353
|
+
console.log('Product deleted');
|
|
354
|
+
|
|
355
|
+
// Bulk operations
|
|
356
|
+
const bulkProducts = [
|
|
357
|
+
new Product({ name: 'Laptop', price: 1299.99 }),
|
|
358
|
+
new Product({ name: 'Tablet', price: 499.99 })
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
const createdProducts = await productService.createAll(bulkProducts);
|
|
362
|
+
console.log('Created multiple products:', createdProducts);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
console.error('Error managing products:', error);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
manageProducts();
|
|
369
|
+
```
|
|
59
370
|
|
|
60
371
|
|
|
61
372
|
### Related
|