@misterzik/espressojs 3.2.5 → 3.3.1
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/CHANGELOG.md +115 -0
- package/CONTRIBUTING.md +96 -0
- package/ENHANCEMENTS.md +262 -0
- package/MIGRATION.md +204 -0
- package/QUICKSTART.md +213 -0
- package/README.md +368 -62
- package/docs/MULTIPLE-APIS.md +451 -0
- package/examples/README.md +89 -0
- package/examples/basic-api.js +116 -0
- package/examples/mongodb-example.js +149 -0
- package/examples/multiple-apis.js +149 -0
- package/index.js +112 -13
- package/package.json +13 -7
- package/routes/index.js +52 -15
- package/server/middleware/errorHandler.js +73 -0
- package/server/middleware/healthCheck.js +71 -0
- package/server/middleware/security.js +60 -0
- package/server/utils/apiManager.js +110 -0
- package/server/utils/config.utils.js +57 -5
- package/server/utils/configValidator.js +90 -0
- package/server/utils/espresso-cli.js +141 -55
- package/server/utils/logger.js +75 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
# Multiple API Endpoints Guide
|
|
2
|
+
|
|
3
|
+
EspressoJS supports configuring and using multiple API endpoints simultaneously. This guide shows you how to set up and use multiple APIs in your application.
|
|
4
|
+
|
|
5
|
+
## 📋 Configuration
|
|
6
|
+
|
|
7
|
+
### Basic Setup
|
|
8
|
+
|
|
9
|
+
In your `config.json`, you can define multiple API endpoints using the pattern `api`, `api2`, `api3`, etc.:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"instance": "production",
|
|
14
|
+
"port": 3001,
|
|
15
|
+
"hostname": "",
|
|
16
|
+
"publicDirectory": "/public",
|
|
17
|
+
"mongoDB": {
|
|
18
|
+
"enabled": false
|
|
19
|
+
},
|
|
20
|
+
"api": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"uri": "https://api.example.com/v1/",
|
|
23
|
+
"method": "GET",
|
|
24
|
+
"headers": {
|
|
25
|
+
"Content-Type": "application/json"
|
|
26
|
+
},
|
|
27
|
+
"timeout": 30000,
|
|
28
|
+
"retries": 2
|
|
29
|
+
},
|
|
30
|
+
"api2": {
|
|
31
|
+
"enabled": true,
|
|
32
|
+
"uri": "https://api.example.com/v1/news",
|
|
33
|
+
"method": "GET",
|
|
34
|
+
"headers": {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
"Authorization": "Bearer YOUR_TOKEN"
|
|
37
|
+
},
|
|
38
|
+
"timeout": 15000,
|
|
39
|
+
"retries": 1
|
|
40
|
+
},
|
|
41
|
+
"api3": {
|
|
42
|
+
"enabled": true,
|
|
43
|
+
"uri": "https://api.example.com/api",
|
|
44
|
+
"method": "POST",
|
|
45
|
+
"headers": {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
"X-Custom-Header": "value"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Configuration Options
|
|
54
|
+
|
|
55
|
+
Each API endpoint supports the following options:
|
|
56
|
+
|
|
57
|
+
| Option | Type | Required | Default | Description |
|
|
58
|
+
|--------|------|----------|---------|-------------|
|
|
59
|
+
| `enabled` | boolean | No | `true` | Enable/disable this API endpoint |
|
|
60
|
+
| `uri` | string | Yes | `""` | Base URL for the API |
|
|
61
|
+
| `method` | string | No | `"GET"` | Default HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) |
|
|
62
|
+
| `headers` | object | No | `{"Content-Type": "application/json"}` | Default headers for requests |
|
|
63
|
+
| `timeout` | number | No | `30000` | Request timeout in milliseconds |
|
|
64
|
+
| `retries` | number | No | `0` | Number of retry attempts on failure (0-5) |
|
|
65
|
+
|
|
66
|
+
## 🚀 Usage
|
|
67
|
+
|
|
68
|
+
### Method 1: Using the API Manager
|
|
69
|
+
|
|
70
|
+
The API Manager is automatically initialized and available in your routes:
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
const { apiManager } = require('../index');
|
|
74
|
+
|
|
75
|
+
// Make a request to a specific API
|
|
76
|
+
router.get('/data', async (req, res) => {
|
|
77
|
+
const data = await apiManager.request('api2', '/endpoint');
|
|
78
|
+
res.json(data);
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Method 2: Multiple Parallel Requests
|
|
83
|
+
|
|
84
|
+
Fetch data from multiple APIs simultaneously:
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
router.get('/combined', async (req, res) => {
|
|
88
|
+
const [data1, data2, data3] = await Promise.all([
|
|
89
|
+
apiManager.request('api', '/users'),
|
|
90
|
+
apiManager.request('api2', '/news'),
|
|
91
|
+
apiManager.request('api3', '/stats')
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
res.json({ data1, data2, data3 });
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Method 3: Custom Request Options
|
|
99
|
+
|
|
100
|
+
Override default configuration per request:
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
router.post('/custom', async (req, res) => {
|
|
104
|
+
const data = await apiManager.request('api', '/endpoint', {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
data: req.body,
|
|
107
|
+
headers: {
|
|
108
|
+
'Custom-Header': 'value'
|
|
109
|
+
},
|
|
110
|
+
timeout: 5000,
|
|
111
|
+
retries: 3
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
res.json(data);
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Method 4: Create Axios Instance
|
|
119
|
+
|
|
120
|
+
For more control, create a custom Axios instance:
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
router.get('/advanced', async (req, res) => {
|
|
124
|
+
const client = apiManager.createAxiosInstance('api2');
|
|
125
|
+
|
|
126
|
+
// Use like regular axios
|
|
127
|
+
const response = await client.get('/endpoint', {
|
|
128
|
+
params: { page: 1, limit: 10 }
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
res.json(response.data);
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 🔍 API Manager Methods
|
|
136
|
+
|
|
137
|
+
### `request(apiName, endpoint, options)`
|
|
138
|
+
|
|
139
|
+
Make a request to a configured API endpoint.
|
|
140
|
+
|
|
141
|
+
**Parameters:**
|
|
142
|
+
- `apiName` (string): Name of the API (e.g., 'api', 'api2', 'api3')
|
|
143
|
+
- `endpoint` (string): Endpoint path to append to base URI
|
|
144
|
+
- `options` (object): Axios request options (optional)
|
|
145
|
+
|
|
146
|
+
**Returns:** Promise with response data
|
|
147
|
+
|
|
148
|
+
**Example:**
|
|
149
|
+
```javascript
|
|
150
|
+
const data = await apiManager.request('api2', '/users/123', {
|
|
151
|
+
method: 'GET',
|
|
152
|
+
headers: { 'X-Custom': 'value' }
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `getAPI(name)`
|
|
157
|
+
|
|
158
|
+
Get configuration for a specific API.
|
|
159
|
+
|
|
160
|
+
**Parameters:**
|
|
161
|
+
- `name` (string): API name (defaults to 'api')
|
|
162
|
+
|
|
163
|
+
**Returns:** API configuration object
|
|
164
|
+
|
|
165
|
+
**Example:**
|
|
166
|
+
```javascript
|
|
167
|
+
const apiConfig = apiManager.getAPI('api2');
|
|
168
|
+
console.log(apiConfig.baseURL); // https://api.example.com/v1/news
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `getAllAPIs()`
|
|
172
|
+
|
|
173
|
+
Get all configured API endpoints.
|
|
174
|
+
|
|
175
|
+
**Returns:** Object with all API configurations
|
|
176
|
+
|
|
177
|
+
**Example:**
|
|
178
|
+
```javascript
|
|
179
|
+
const allAPIs = apiManager.getAllAPIs();
|
|
180
|
+
console.log(Object.keys(allAPIs)); // ['api', 'api2', 'api3']
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### `hasAPI(name)`
|
|
184
|
+
|
|
185
|
+
Check if an API is configured.
|
|
186
|
+
|
|
187
|
+
**Parameters:**
|
|
188
|
+
- `name` (string): API name to check
|
|
189
|
+
|
|
190
|
+
**Returns:** Boolean
|
|
191
|
+
|
|
192
|
+
**Example:**
|
|
193
|
+
```javascript
|
|
194
|
+
if (apiManager.hasAPI('api2')) {
|
|
195
|
+
// Use api2
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### `createAxiosInstance(apiName)`
|
|
200
|
+
|
|
201
|
+
Create a custom Axios instance for an API.
|
|
202
|
+
|
|
203
|
+
**Parameters:**
|
|
204
|
+
- `apiName` (string): Name of the API
|
|
205
|
+
|
|
206
|
+
**Returns:** Axios instance
|
|
207
|
+
|
|
208
|
+
**Example:**
|
|
209
|
+
```javascript
|
|
210
|
+
const client = apiManager.createAxiosInstance('api3');
|
|
211
|
+
const response = await client.post('/data', { key: 'value' });
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## 💡 Common Patterns
|
|
215
|
+
|
|
216
|
+
### Fallback Pattern
|
|
217
|
+
|
|
218
|
+
Try primary API, fallback to secondary:
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
router.get('/data', async (req, res) => {
|
|
222
|
+
let data;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
data = await apiManager.request('api', '/data');
|
|
226
|
+
} catch (error) {
|
|
227
|
+
// Fallback to api2
|
|
228
|
+
data = await apiManager.request('api2', '/data');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
res.json(data);
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Conditional API Usage
|
|
236
|
+
|
|
237
|
+
Use different APIs based on conditions:
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
router.get('/data/:type', async (req, res) => {
|
|
241
|
+
const { type } = req.params;
|
|
242
|
+
|
|
243
|
+
let apiName = 'api';
|
|
244
|
+
if (type === 'news') apiName = 'api2';
|
|
245
|
+
if (type === 'stats') apiName = 'api3';
|
|
246
|
+
|
|
247
|
+
const data = await apiManager.request(apiName, '/data');
|
|
248
|
+
res.json(data);
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Error Handling with Retries
|
|
253
|
+
|
|
254
|
+
The API Manager automatically retries failed requests based on the `retries` configuration:
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
// This will retry up to 2 times with exponential backoff
|
|
258
|
+
const data = await apiManager.request('api', '/endpoint', {
|
|
259
|
+
retries: 2
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Proxy Pattern
|
|
264
|
+
|
|
265
|
+
Create a proxy endpoint for any configured API:
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
router.all('/proxy/:apiName/*', async (req, res) => {
|
|
269
|
+
const { apiName } = req.params;
|
|
270
|
+
const endpoint = req.params[0];
|
|
271
|
+
|
|
272
|
+
if (!apiManager.hasAPI(apiName)) {
|
|
273
|
+
return res.status(404).json({ error: 'API not found' });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const data = await apiManager.request(apiName, `/${endpoint}`, {
|
|
277
|
+
method: req.method,
|
|
278
|
+
data: req.body,
|
|
279
|
+
params: req.query
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
res.json(data);
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## 🔒 Security Best Practices
|
|
287
|
+
|
|
288
|
+
### 1. Use Environment Variables for Tokens
|
|
289
|
+
|
|
290
|
+
Store sensitive data in `.env`:
|
|
291
|
+
|
|
292
|
+
```env
|
|
293
|
+
API_TOKEN=your_secret_token
|
|
294
|
+
API2_KEY=another_secret_key
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Reference in `config.json`:
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"api2": {
|
|
302
|
+
"headers": {
|
|
303
|
+
"Authorization": "Bearer ${API_TOKEN}"
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Or set headers dynamically in your code:
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
const data = await apiManager.request('api2', '/endpoint', {
|
|
313
|
+
headers: {
|
|
314
|
+
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 2. Set Appropriate Timeouts
|
|
320
|
+
|
|
321
|
+
Configure timeouts to prevent hanging requests:
|
|
322
|
+
|
|
323
|
+
```json
|
|
324
|
+
{
|
|
325
|
+
"api": {
|
|
326
|
+
"timeout": 5000
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 3. Limit Retries
|
|
332
|
+
|
|
333
|
+
Set reasonable retry limits to avoid overwhelming APIs:
|
|
334
|
+
|
|
335
|
+
```json
|
|
336
|
+
{
|
|
337
|
+
"api": {
|
|
338
|
+
"retries": 2
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## 📊 Monitoring
|
|
344
|
+
|
|
345
|
+
### Log API Requests
|
|
346
|
+
|
|
347
|
+
The API Manager automatically logs all requests:
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
[info]: API endpoint 'api2' loaded: https://api.example.com/v1/news
|
|
351
|
+
[debug]: API request to api2: GET https://api.example.com/v1/news/latest (attempt 1/1)
|
|
352
|
+
[info]: API api2 request successful: 200
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Track API Health
|
|
356
|
+
|
|
357
|
+
Create a health check endpoint:
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
router.get('/api-health', async (req, res) => {
|
|
361
|
+
const apis = apiManager.getAllAPIs();
|
|
362
|
+
const health = {};
|
|
363
|
+
|
|
364
|
+
for (const [name, config] of Object.entries(apis)) {
|
|
365
|
+
try {
|
|
366
|
+
await apiManager.request(name, '/health', { timeout: 5000 });
|
|
367
|
+
health[name] = 'healthy';
|
|
368
|
+
} catch (error) {
|
|
369
|
+
health[name] = 'unhealthy';
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
res.json({ status: 'ok', apis: health });
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## 🎯 Real-World Example
|
|
378
|
+
|
|
379
|
+
Here's a complete example using the client's production configuration:
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
const express = require('express');
|
|
383
|
+
const router = express.Router();
|
|
384
|
+
const { apiManager } = require('../index');
|
|
385
|
+
const { asyncHandler } = require('../server/middleware/errorHandler');
|
|
386
|
+
|
|
387
|
+
// Get exchange data from primary API
|
|
388
|
+
router.get('/exchange', asyncHandler(async (req, res) => {
|
|
389
|
+
const data = await apiManager.request('api', '');
|
|
390
|
+
res.json(data);
|
|
391
|
+
}));
|
|
392
|
+
|
|
393
|
+
// Get news from api2
|
|
394
|
+
router.get('/news', asyncHandler(async (req, res) => {
|
|
395
|
+
const news = await apiManager.request('api2', '');
|
|
396
|
+
res.json(news);
|
|
397
|
+
}));
|
|
398
|
+
|
|
399
|
+
// Get general API data from api3
|
|
400
|
+
router.get('/general', asyncHandler(async (req, res) => {
|
|
401
|
+
const data = await apiManager.request('api3', '');
|
|
402
|
+
res.json(data);
|
|
403
|
+
}));
|
|
404
|
+
|
|
405
|
+
// Combined dashboard data
|
|
406
|
+
router.get('/dashboard', asyncHandler(async (req, res) => {
|
|
407
|
+
const [exchange, news, general] = await Promise.allSettled([
|
|
408
|
+
apiManager.request('api', ''),
|
|
409
|
+
apiManager.request('api2', ''),
|
|
410
|
+
apiManager.request('api3', '')
|
|
411
|
+
]);
|
|
412
|
+
|
|
413
|
+
res.json({
|
|
414
|
+
exchange: exchange.status === 'fulfilled' ? exchange.value : null,
|
|
415
|
+
news: news.status === 'fulfilled' ? news.value : null,
|
|
416
|
+
general: general.status === 'fulfilled' ? general.value : null
|
|
417
|
+
});
|
|
418
|
+
}));
|
|
419
|
+
|
|
420
|
+
module.exports = router;
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## 🆘 Troubleshooting
|
|
424
|
+
|
|
425
|
+
### API not found error
|
|
426
|
+
|
|
427
|
+
**Error:** `API 'api2' not found in configuration`
|
|
428
|
+
|
|
429
|
+
**Solution:** Ensure the API is defined in `config.json` and `enabled` is not set to `false`.
|
|
430
|
+
|
|
431
|
+
### Timeout errors
|
|
432
|
+
|
|
433
|
+
**Error:** Request timeout
|
|
434
|
+
|
|
435
|
+
**Solution:** Increase the `timeout` value in your API configuration or request options.
|
|
436
|
+
|
|
437
|
+
### Validation errors
|
|
438
|
+
|
|
439
|
+
**Error:** Configuration validation failed
|
|
440
|
+
|
|
441
|
+
**Solution:** Run `node cli validate` to check your configuration for errors.
|
|
442
|
+
|
|
443
|
+
## 📚 See Also
|
|
444
|
+
|
|
445
|
+
- [Basic API Example](../examples/basic-api.js)
|
|
446
|
+
- [Multiple APIs Example](../examples/multiple-apis.js)
|
|
447
|
+
- [Configuration Guide](../README.md#configuration)
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
**Need help?** Open an issue on [GitHub](https://github.com/misterzik/Espresso.js/issues)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# EspressoJS Examples
|
|
2
|
+
|
|
3
|
+
This directory contains example implementations to help you get started with EspressoJS.
|
|
4
|
+
|
|
5
|
+
## 📁 Available Examples
|
|
6
|
+
|
|
7
|
+
### `basic-api.js`
|
|
8
|
+
Demonstrates how to create RESTful API routes with:
|
|
9
|
+
- GET, POST, PUT, DELETE operations
|
|
10
|
+
- Request validation using express-validator
|
|
11
|
+
- Error handling with asyncHandler
|
|
12
|
+
- Proper response formatting
|
|
13
|
+
|
|
14
|
+
**Usage:**
|
|
15
|
+
```javascript
|
|
16
|
+
// In your routes/api.js
|
|
17
|
+
module.exports = require('./examples/basic-api');
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### `mongodb-example.js`
|
|
21
|
+
Shows MongoDB integration with Mongoose:
|
|
22
|
+
- Schema definition and validation
|
|
23
|
+
- CRUD operations
|
|
24
|
+
- Pagination
|
|
25
|
+
- Search functionality
|
|
26
|
+
- Error handling
|
|
27
|
+
|
|
28
|
+
**Usage:**
|
|
29
|
+
```javascript
|
|
30
|
+
// In your routes/db.js
|
|
31
|
+
module.exports = require('./examples/mongodb-example');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `multiple-apis.js` ⭐ NEW
|
|
35
|
+
Demonstrates using multiple API endpoints:
|
|
36
|
+
- Making requests to different APIs (api, api2, api3)
|
|
37
|
+
- Parallel API requests
|
|
38
|
+
- Custom request options
|
|
39
|
+
- Creating Axios instances
|
|
40
|
+
- Conditional API usage and fallbacks
|
|
41
|
+
- API proxy patterns
|
|
42
|
+
- Error handling with retries
|
|
43
|
+
|
|
44
|
+
**Usage:**
|
|
45
|
+
```javascript
|
|
46
|
+
// In your routes/api.js
|
|
47
|
+
module.exports = require('./examples/multiple-apis');
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Configuration:**
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"api": {
|
|
54
|
+
"enabled": true,
|
|
55
|
+
"uri": "https://api.example.com/v1/"
|
|
56
|
+
},
|
|
57
|
+
"api2": {
|
|
58
|
+
"uri": "https://api.example.com/v1/news"
|
|
59
|
+
},
|
|
60
|
+
"api3": {
|
|
61
|
+
"uri": "https://api.example.com/api"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 🚀 Getting Started
|
|
67
|
+
|
|
68
|
+
1. Copy the example file you want to use
|
|
69
|
+
2. Place it in your `routes/` directory
|
|
70
|
+
3. Customize it for your needs
|
|
71
|
+
4. Enable the routes in your `config.json`
|
|
72
|
+
|
|
73
|
+
## 💡 Tips
|
|
74
|
+
|
|
75
|
+
- Use `asyncHandler` for all async route handlers
|
|
76
|
+
- Validate input with express-validator
|
|
77
|
+
- Return consistent response formats
|
|
78
|
+
- Use proper HTTP status codes
|
|
79
|
+
- Handle errors gracefully
|
|
80
|
+
|
|
81
|
+
## 📚 Learn More
|
|
82
|
+
|
|
83
|
+
- [Express.js Documentation](https://expressjs.com/)
|
|
84
|
+
- [Mongoose Documentation](https://mongoosejs.com/)
|
|
85
|
+
- [Express Validator](https://express-validator.github.io/)
|
|
86
|
+
|
|
87
|
+
## 🤝 Contributing
|
|
88
|
+
|
|
89
|
+
Have a useful example? Submit a PR to share it with the community!
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* EspressoJS - Example API Routes
|
|
3
|
+
* This file demonstrates how to create custom API routes
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const express = require('express');
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
const { body, validationResult } = require('express-validator');
|
|
9
|
+
const { asyncHandler, AppError } = require('../server/middleware/errorHandler');
|
|
10
|
+
|
|
11
|
+
// Example: GET all items
|
|
12
|
+
router.get('/items', asyncHandler(async (req, res) => {
|
|
13
|
+
const items = [
|
|
14
|
+
{ id: 1, name: 'Item 1', description: 'First item' },
|
|
15
|
+
{ id: 2, name: 'Item 2', description: 'Second item' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
res.status(200).json({
|
|
19
|
+
status: 'success',
|
|
20
|
+
results: items.length,
|
|
21
|
+
data: { items }
|
|
22
|
+
});
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// Example: GET single item by ID
|
|
26
|
+
router.get('/items/:id', asyncHandler(async (req, res) => {
|
|
27
|
+
const { id } = req.params;
|
|
28
|
+
|
|
29
|
+
// Simulate database lookup
|
|
30
|
+
const item = { id, name: `Item ${id}`, description: 'Example item' };
|
|
31
|
+
|
|
32
|
+
if (!item) {
|
|
33
|
+
throw new AppError('Item not found', 404);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
res.status(200).json({
|
|
37
|
+
status: 'success',
|
|
38
|
+
data: { item }
|
|
39
|
+
});
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
// Example: POST create new item with validation
|
|
43
|
+
router.post('/items',
|
|
44
|
+
[
|
|
45
|
+
body('name').notEmpty().withMessage('Name is required'),
|
|
46
|
+
body('description').optional().isLength({ max: 500 })
|
|
47
|
+
],
|
|
48
|
+
asyncHandler(async (req, res) => {
|
|
49
|
+
const errors = validationResult(req);
|
|
50
|
+
|
|
51
|
+
if (!errors.isEmpty()) {
|
|
52
|
+
return res.status(400).json({
|
|
53
|
+
status: 'fail',
|
|
54
|
+
errors: errors.array()
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { name, description } = req.body;
|
|
59
|
+
|
|
60
|
+
// Simulate database insert
|
|
61
|
+
const newItem = {
|
|
62
|
+
id: Date.now(),
|
|
63
|
+
name,
|
|
64
|
+
description,
|
|
65
|
+
createdAt: new Date().toISOString()
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
res.status(201).json({
|
|
69
|
+
status: 'success',
|
|
70
|
+
data: { item: newItem }
|
|
71
|
+
});
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Example: PUT update item
|
|
76
|
+
router.put('/items/:id',
|
|
77
|
+
[
|
|
78
|
+
body('name').optional().notEmpty(),
|
|
79
|
+
body('description').optional().isLength({ max: 500 })
|
|
80
|
+
],
|
|
81
|
+
asyncHandler(async (req, res) => {
|
|
82
|
+
const errors = validationResult(req);
|
|
83
|
+
|
|
84
|
+
if (!errors.isEmpty()) {
|
|
85
|
+
return res.status(400).json({
|
|
86
|
+
status: 'fail',
|
|
87
|
+
errors: errors.array()
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const { id } = req.params;
|
|
92
|
+
const updates = req.body;
|
|
93
|
+
|
|
94
|
+
// Simulate database update
|
|
95
|
+
const updatedItem = {
|
|
96
|
+
id,
|
|
97
|
+
...updates,
|
|
98
|
+
updatedAt: new Date().toISOString()
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
res.status(200).json({
|
|
102
|
+
status: 'success',
|
|
103
|
+
data: { item: updatedItem }
|
|
104
|
+
});
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Example: DELETE item
|
|
109
|
+
router.delete('/items/:id', asyncHandler(async (req, res) => {
|
|
110
|
+
const { id } = req.params;
|
|
111
|
+
|
|
112
|
+
// Simulate database delete
|
|
113
|
+
res.status(204).send();
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
module.exports = router;
|