api-json-server 1.0.1 → 1.2.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 +636 -0
- package/dist/behavior.js +44 -0
- package/dist/history/historyRecorder.js +66 -0
- package/dist/history/types.js +2 -0
- package/dist/index.js +126 -5
- package/dist/loadSpec.js +32 -0
- package/dist/logger/customLogger.js +75 -0
- package/dist/logger/formatters.js +82 -0
- package/dist/logger/types.js +2 -0
- package/dist/openapi.js +152 -0
- package/dist/registerEndpoints.js +97 -0
- package/dist/requestMatch.js +99 -0
- package/dist/responseRenderer.js +98 -0
- package/dist/server.js +210 -0
- package/dist/spec.js +146 -0
- package/dist/stringTemplate.js +55 -0
- package/examples/auth-variants.json +31 -0
- package/examples/basic-crud.json +46 -0
- package/examples/companies-nested.json +47 -0
- package/examples/orders-and-matches.json +49 -0
- package/examples/users-faker.json +35 -0
- package/mock.spec.json +1 -0
- package/mockserve.spec.schema.json +7 -0
- package/package.json +20 -3
- package/scripts/build-schema.ts +21 -0
- package/src/behavior.ts +56 -0
- package/src/history/historyRecorder.ts +77 -0
- package/src/history/types.ts +25 -0
- package/src/index.ts +124 -85
- package/src/loadSpec.ts +5 -2
- package/src/logger/customLogger.ts +85 -0
- package/src/logger/formatters.ts +74 -0
- package/src/logger/types.ts +30 -0
- package/src/openapi.ts +203 -0
- package/src/registerEndpoints.ts +94 -162
- package/src/requestMatch.ts +104 -0
- package/src/responseRenderer.ts +112 -0
- package/src/server.ts +236 -14
- package/src/spec.ts +108 -8
- package/src/stringTemplate.ts +55 -0
- package/tests/behavior.test.ts +88 -0
- package/tests/cors.test.ts +128 -0
- package/tests/faker.test.ts +175 -0
- package/tests/fixtures/spec.basic.json +39 -0
- package/tests/headers.test.ts +124 -0
- package/tests/helpers.ts +28 -0
- package/tests/history.test.ts +188 -0
- package/tests/matching.test.ts +245 -0
- package/tests/server.test.ts +73 -0
- package/tests/template.test.ts +90 -0
- package/src/template.ts +0 -61
package/README.md
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
# api-json-server
|
|
2
|
+
|
|
3
|
+
A powerful, feature-rich mock API server driven by a JSON spec. Designed for fast development, repeatable mock data, comprehensive testing, and clear API documentation.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- **JSON-driven spec** - Define endpoints, responses, and matching rules in a simple JSON file
|
|
8
|
+
- **Advanced matching** - Match requests by query params, body, headers, and cookies
|
|
9
|
+
- **Custom headers** - Set response headers with template support
|
|
10
|
+
- **CORS configuration** - Global and per-endpoint CORS settings
|
|
11
|
+
- **Faker integration** - Generate realistic fake data (names, emails, phone numbers, companies, dates, etc.)
|
|
12
|
+
- **Response templating** - Use request data (params, query, body) in responses
|
|
13
|
+
- **Variants** - Define alternate responses based on match rules
|
|
14
|
+
- **Request history** - Record and inspect all incoming requests for debugging
|
|
15
|
+
- **Variable delays** - Simulate realistic network latency with random delay ranges
|
|
16
|
+
- **Error simulation** - Control error rates and responses
|
|
17
|
+
- **OpenAPI/Swagger** - Auto-generated OpenAPI docs with built-in Swagger UI
|
|
18
|
+
- **Hot reload** - Auto-reload when spec file changes (with `--watch`)
|
|
19
|
+
- **Beautiful logging** - Color-coded, readable console output
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
1) Create a spec file (`mock.spec.json`):
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"version": 1,
|
|
34
|
+
"settings": {
|
|
35
|
+
"delayMs": 0,
|
|
36
|
+
"errorRate": 0
|
|
37
|
+
},
|
|
38
|
+
"endpoints": [
|
|
39
|
+
{
|
|
40
|
+
"method": "GET",
|
|
41
|
+
"path": "/users/:id",
|
|
42
|
+
"response": {
|
|
43
|
+
"id": "{{params.id}}",
|
|
44
|
+
"name": { "__faker": "person.fullName" },
|
|
45
|
+
"email": { "__faker": "internet.email" },
|
|
46
|
+
"avatar": { "__faker": "image.avatar" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2) Start the server:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm run dev -- serve --spec mock.spec.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
3) Test the endpoint:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
curl "http://localhost:3000/users/42"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Response:
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"id": "42",
|
|
69
|
+
"name": "Jane Doe",
|
|
70
|
+
"email": "jane.doe@example.com",
|
|
71
|
+
"avatar": "https://cloudflare-ipfs.com/ipfs/..."
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## CLI
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
mockserve serve [options]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Options
|
|
82
|
+
|
|
83
|
+
- `--spec <path>` - Path to the JSON spec file (default: `mock.spec.json`)
|
|
84
|
+
- `--port <number>` - Port to run the server on (default: `3000`)
|
|
85
|
+
- `--watch` / `--no-watch` - Auto-reload when spec changes (default: enabled)
|
|
86
|
+
- `--base-url <url>` - Public base URL for OpenAPI servers[] (e.g., `https://api.example.com`)
|
|
87
|
+
- `--log-format <format>` - Log format: `pretty` or `json` (default: `pretty`)
|
|
88
|
+
- `--log-level <level>` - Log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal` (default: `info`)
|
|
89
|
+
|
|
90
|
+
### Examples
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Start with custom port
|
|
94
|
+
mockserve serve --port 8080
|
|
95
|
+
|
|
96
|
+
# Use JSON logging format
|
|
97
|
+
mockserve serve --log-format json
|
|
98
|
+
|
|
99
|
+
# Disable auto-reload
|
|
100
|
+
mockserve serve --no-watch
|
|
101
|
+
|
|
102
|
+
# Set base URL for OpenAPI
|
|
103
|
+
mockserve serve --base-url https://api.mysite.com
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Spec Reference
|
|
107
|
+
|
|
108
|
+
The spec is validated by `mockserve.spec.schema.json`. It consists of:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"version": 1,
|
|
113
|
+
"settings": { ... },
|
|
114
|
+
"endpoints": [ ... ]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Settings
|
|
119
|
+
|
|
120
|
+
Global settings that apply to all endpoints unless overridden:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"delayMs": 0,
|
|
125
|
+
"errorRate": 0,
|
|
126
|
+
"errorStatus": 500,
|
|
127
|
+
"errorResponse": { "error": "Mock error" },
|
|
128
|
+
"fakerSeed": 12345,
|
|
129
|
+
"cors": {
|
|
130
|
+
"origin": "*",
|
|
131
|
+
"credentials": true
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### Settings Fields
|
|
137
|
+
|
|
138
|
+
- **`delayMs`** (number): Fixed delay in milliseconds before responding
|
|
139
|
+
- **`errorRate`** (number): Probability (0.0-1.0) of returning an error response
|
|
140
|
+
- **`errorStatus`** (number): HTTP status code for simulated errors
|
|
141
|
+
- **`errorResponse`** (any): Response body when error is triggered (supports templates)
|
|
142
|
+
- **`fakerSeed`** (number, optional): Seed for deterministic faker data generation
|
|
143
|
+
- **`cors`** (object, optional): CORS configuration
|
|
144
|
+
- `origin` (string | string[] | boolean): Allowed origins (`"*"`, `"https://example.com"`, or array)
|
|
145
|
+
- `credentials` (boolean): Allow credentials
|
|
146
|
+
- `methods` (string[]): Allowed HTTP methods
|
|
147
|
+
- `allowedHeaders` (string[]): Allowed request headers
|
|
148
|
+
- `exposedHeaders` (string[]): Exposed response headers
|
|
149
|
+
- `maxAge` (number): Preflight cache duration in seconds
|
|
150
|
+
|
|
151
|
+
### Endpoints
|
|
152
|
+
|
|
153
|
+
Each endpoint defines a route with its matching rules and response behavior:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"method": "GET",
|
|
158
|
+
"path": "/users/:id",
|
|
159
|
+
"match": {
|
|
160
|
+
"query": { "type": "premium" },
|
|
161
|
+
"headers": { "Authorization": "Bearer token123" },
|
|
162
|
+
"cookies": { "sessionId": "valid" }
|
|
163
|
+
},
|
|
164
|
+
"status": 200,
|
|
165
|
+
"response": { ... },
|
|
166
|
+
"headers": {
|
|
167
|
+
"X-Custom-Header": "value",
|
|
168
|
+
"Cache-Control": "no-cache"
|
|
169
|
+
},
|
|
170
|
+
"delay": { "min": 100, "max": 500 },
|
|
171
|
+
"delayMs": 0,
|
|
172
|
+
"errorRate": 0,
|
|
173
|
+
"errorStatus": 500,
|
|
174
|
+
"errorResponse": { "error": "Not found" },
|
|
175
|
+
"variants": [ ... ]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Endpoint Fields
|
|
180
|
+
|
|
181
|
+
- **`method`** (string): HTTP method - `GET`, `POST`, `PUT`, `PATCH`, `DELETE`
|
|
182
|
+
- **`path`** (string): Route path with Fastify-style params (e.g., `/users/:id`)
|
|
183
|
+
- **`match`** (object, optional): Request matching rules (see [Match Rules](#match-rules))
|
|
184
|
+
- **`status`** (number): Default HTTP status code (default: `200`)
|
|
185
|
+
- **`response`** (any): Response body (supports templates)
|
|
186
|
+
- **`headers`** (object, optional): Custom response headers (supports templates in values)
|
|
187
|
+
- **`delay`** (object, optional): Variable delay range - `{ "min": 100, "max": 500 }`
|
|
188
|
+
- **`delayMs`** (number, optional): Fixed delay in milliseconds
|
|
189
|
+
- **`errorRate`** (number, optional): Error probability override
|
|
190
|
+
- **`errorStatus`** (number, optional): Error status override
|
|
191
|
+
- **`errorResponse`** (any, optional): Error response override
|
|
192
|
+
- **`variants`** (array, optional): Alternative responses with their own match rules
|
|
193
|
+
|
|
194
|
+
### Match Rules
|
|
195
|
+
|
|
196
|
+
Match rules determine if a request should be handled by an endpoint or variant. All specified match conditions must be satisfied.
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
"match": {
|
|
200
|
+
"query": { "type": "premium", "status": "active" },
|
|
201
|
+
"body": { "email": "test@example.com" },
|
|
202
|
+
"headers": { "Authorization": "Bearer secret" },
|
|
203
|
+
"cookies": { "sessionId": "abc123" }
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### Match Fields
|
|
208
|
+
|
|
209
|
+
- **`query`** (object): Exact match for query parameters (strings, numbers, booleans)
|
|
210
|
+
- **`body`** (object): Exact match for top-level body fields
|
|
211
|
+
- **`headers`** (object): Case-insensitive exact match for headers
|
|
212
|
+
- **`cookies`** (object): Exact match for cookies
|
|
213
|
+
|
|
214
|
+
If a request doesn't satisfy the match rules, the server returns `404` with:
|
|
215
|
+
```json
|
|
216
|
+
{ "error": "No matching mock for request" }
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Variants
|
|
220
|
+
|
|
221
|
+
Variants provide alternative responses based on match rules. The **first matching variant wins**. If no variant matches, the endpoint's base response is used.
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
"variants": [
|
|
225
|
+
{
|
|
226
|
+
"name": "admin user",
|
|
227
|
+
"match": { "body": { "role": "admin" } },
|
|
228
|
+
"status": 200,
|
|
229
|
+
"response": { "ok": true, "role": "admin", "permissions": ["*"] },
|
|
230
|
+
"headers": { "X-User-Role": "admin" }
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"name": "invalid credentials",
|
|
234
|
+
"match": { "body": { "password": "wrong" } },
|
|
235
|
+
"status": 401,
|
|
236
|
+
"response": { "ok": false, "error": "Invalid credentials" }
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### Variant Fields
|
|
242
|
+
|
|
243
|
+
- **`name`** (string, optional): Descriptive name for the variant
|
|
244
|
+
- **`match`** (object, optional): Match rules (same as endpoint-level)
|
|
245
|
+
- **`status`** (number, optional): HTTP status code
|
|
246
|
+
- **`response`** (any): Response body (supports templates)
|
|
247
|
+
- **`headers`** (object, optional): Custom response headers (overrides endpoint headers)
|
|
248
|
+
- **`delay`** / **`delayMs`** (optional): Delay overrides
|
|
249
|
+
- **`errorRate`**, **`errorStatus`**, **`errorResponse`** (optional): Error simulation overrides
|
|
250
|
+
|
|
251
|
+
### Response Templates
|
|
252
|
+
|
|
253
|
+
Responses support a powerful templating system combining static values, request data, faker directives, and array generation.
|
|
254
|
+
|
|
255
|
+
#### String Placeholders
|
|
256
|
+
|
|
257
|
+
Access request data using mustache-style placeholders:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"userId": "{{params.id}}",
|
|
262
|
+
"searchType": "{{query.type}}",
|
|
263
|
+
"userEmail": "{{body.email}}"
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Available contexts:**
|
|
268
|
+
- `{{params.name}}` - Path parameters
|
|
269
|
+
- `{{query.key}}` - Query string parameters
|
|
270
|
+
- `{{body.field}}` - Request body fields (supports nested paths like `{{body.user.name}}`)
|
|
271
|
+
|
|
272
|
+
#### Faker Directives
|
|
273
|
+
|
|
274
|
+
Generate realistic fake data using any `@faker-js/faker` method:
|
|
275
|
+
|
|
276
|
+
**Simple syntax:**
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"name": { "__faker": "person.fullName" },
|
|
280
|
+
"email": { "__faker": "internet.email" },
|
|
281
|
+
"phone": { "__faker": "phone.number" },
|
|
282
|
+
"company": { "__faker": "company.name" },
|
|
283
|
+
"avatar": { "__faker": "image.avatar" },
|
|
284
|
+
"birthdate": { "__faker": "date.birthdate" },
|
|
285
|
+
"city": { "__faker": "location.city" }
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**With arguments:**
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"randomString": { "__faker": { "method": "string.alpha", "args": [16] } },
|
|
293
|
+
"price": { "__faker": { "method": "number.float", "args": [{ "min": 10, "max": 100, "precision": 0.01 }] } }
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Deterministic output:**
|
|
298
|
+
Set `fakerSeed` in settings for consistent data across requests:
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"settings": {
|
|
302
|
+
"fakerSeed": 12345
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### Repeat Directives
|
|
308
|
+
|
|
309
|
+
Generate arrays of items with random or fixed counts:
|
|
310
|
+
|
|
311
|
+
**Random count (min/max range):**
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"users": {
|
|
315
|
+
"__repeat": {
|
|
316
|
+
"min": 10,
|
|
317
|
+
"max": 15,
|
|
318
|
+
"template": {
|
|
319
|
+
"id": { "__faker": "string.uuid" },
|
|
320
|
+
"name": { "__faker": "person.fullName" },
|
|
321
|
+
"email": { "__faker": "internet.email" }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Fixed count:**
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"tags": {
|
|
332
|
+
"__repeat": {
|
|
333
|
+
"count": 3,
|
|
334
|
+
"template": { "__faker": "lorem.word" }
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Combining templates:**
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"userId": "{{params.id}}",
|
|
344
|
+
"orders": {
|
|
345
|
+
"__repeat": {
|
|
346
|
+
"min": 5,
|
|
347
|
+
"max": 10,
|
|
348
|
+
"template": {
|
|
349
|
+
"orderId": { "__faker": "string.uuid" },
|
|
350
|
+
"amount": { "__faker": { "method": "number.float", "args": [{ "min": 10, "max": 1000, "precision": 0.01 }] } },
|
|
351
|
+
"status": { "__faker": { "method": "helpers.arrayElement", "args": [["pending", "shipped", "delivered"]] } }
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Advanced Features
|
|
359
|
+
|
|
360
|
+
### Custom Response Headers
|
|
361
|
+
|
|
362
|
+
Set custom headers on responses, with support for templating:
|
|
363
|
+
|
|
364
|
+
```json
|
|
365
|
+
{
|
|
366
|
+
"method": "GET",
|
|
367
|
+
"path": "/api/data/:id",
|
|
368
|
+
"response": { "data": "value" },
|
|
369
|
+
"headers": {
|
|
370
|
+
"X-Resource-ID": "{{params.id}}",
|
|
371
|
+
"X-Request-Type": "{{query.type}}",
|
|
372
|
+
"Cache-Control": "max-age=3600",
|
|
373
|
+
"X-Custom-Header": "static-value"
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Variant headers override endpoint headers:
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"method": "GET",
|
|
383
|
+
"path": "/api/data",
|
|
384
|
+
"headers": { "X-Source": "base" },
|
|
385
|
+
"variants": [
|
|
386
|
+
{
|
|
387
|
+
"match": { "query": { "premium": "true" } },
|
|
388
|
+
"response": { "data": "premium" },
|
|
389
|
+
"headers": { "X-Source": "premium", "X-Tier": "gold" }
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Variable Delays
|
|
396
|
+
|
|
397
|
+
Simulate realistic network latency with random delay ranges:
|
|
398
|
+
|
|
399
|
+
```json
|
|
400
|
+
{
|
|
401
|
+
"method": "GET",
|
|
402
|
+
"path": "/api/slow",
|
|
403
|
+
"delay": { "min": 100, "max": 500 },
|
|
404
|
+
"response": { "ok": true }
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
The server will wait a random duration between 100ms and 500ms before responding.
|
|
409
|
+
|
|
410
|
+
### Request History
|
|
411
|
+
|
|
412
|
+
All requests are automatically recorded and can be inspected via the history endpoint:
|
|
413
|
+
|
|
414
|
+
**View all requests:**
|
|
415
|
+
```bash
|
|
416
|
+
GET /__history
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Filter by endpoint:**
|
|
420
|
+
```bash
|
|
421
|
+
GET /__history?endpoint=/api/users
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Filter by method:**
|
|
425
|
+
```bash
|
|
426
|
+
GET /__history?method=POST
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Limit results:**
|
|
430
|
+
```bash
|
|
431
|
+
GET /__history?limit=10
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**Clear history:**
|
|
435
|
+
```bash
|
|
436
|
+
DELETE /__history
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**History entry format:**
|
|
440
|
+
```json
|
|
441
|
+
{
|
|
442
|
+
"entries": [
|
|
443
|
+
{
|
|
444
|
+
"id": "uuid",
|
|
445
|
+
"timestamp": "2026-01-18T14:30:00.000Z",
|
|
446
|
+
"method": "POST",
|
|
447
|
+
"url": "/api/users",
|
|
448
|
+
"path": "/api/users",
|
|
449
|
+
"query": {},
|
|
450
|
+
"headers": { "content-type": "application/json" },
|
|
451
|
+
"body": { "name": "John" },
|
|
452
|
+
"statusCode": 201,
|
|
453
|
+
"responseTime": 45
|
|
454
|
+
}
|
|
455
|
+
],
|
|
456
|
+
"total": 1
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### CORS Configuration
|
|
461
|
+
|
|
462
|
+
Enable CORS globally or per-endpoint:
|
|
463
|
+
|
|
464
|
+
**Global CORS:**
|
|
465
|
+
```json
|
|
466
|
+
{
|
|
467
|
+
"settings": {
|
|
468
|
+
"cors": {
|
|
469
|
+
"origin": "*",
|
|
470
|
+
"credentials": true,
|
|
471
|
+
"methods": ["GET", "POST", "PUT", "DELETE"],
|
|
472
|
+
"allowedHeaders": ["Content-Type", "Authorization"]
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
**Per-endpoint CORS:**
|
|
479
|
+
```json
|
|
480
|
+
{
|
|
481
|
+
"method": "GET",
|
|
482
|
+
"path": "/api/public",
|
|
483
|
+
"cors": { "origin": "https://example.com" },
|
|
484
|
+
"response": { "data": "public" }
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Error Simulation
|
|
489
|
+
|
|
490
|
+
Simulate random errors for reliability testing:
|
|
491
|
+
|
|
492
|
+
**Global error rate:**
|
|
493
|
+
```json
|
|
494
|
+
{
|
|
495
|
+
"settings": {
|
|
496
|
+
"errorRate": 0.1,
|
|
497
|
+
"errorStatus": 503,
|
|
498
|
+
"errorResponse": { "error": "Service temporarily unavailable" }
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**Per-endpoint error rate:**
|
|
504
|
+
```json
|
|
505
|
+
{
|
|
506
|
+
"method": "GET",
|
|
507
|
+
"path": "/api/unstable",
|
|
508
|
+
"errorRate": 0.5,
|
|
509
|
+
"errorStatus": 500,
|
|
510
|
+
"errorResponse": { "error": "Internal server error" },
|
|
511
|
+
"response": { "ok": true }
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
10% of requests will return the error response with the specified status code.
|
|
516
|
+
|
|
517
|
+
## Built-in Endpoints
|
|
518
|
+
|
|
519
|
+
mockserve provides several special endpoints for inspection and debugging:
|
|
520
|
+
|
|
521
|
+
- **`GET /health`** - Health check endpoint (returns `{ "ok": true }`)
|
|
522
|
+
- **`GET /__spec`** - View the loaded spec and metadata
|
|
523
|
+
- **`GET /__openapi.json`** - OpenAPI 3.0 specification in JSON format
|
|
524
|
+
- **`GET /__openapi.yaml`** - OpenAPI 3.0 specification in YAML format
|
|
525
|
+
- **`GET /docs`** - Interactive Swagger UI documentation
|
|
526
|
+
- **`GET /__history`** - View request history (supports filtering)
|
|
527
|
+
- **`DELETE /__history`** - Clear request history
|
|
528
|
+
|
|
529
|
+
## Examples
|
|
530
|
+
|
|
531
|
+
The `examples/` folder contains ready-to-use spec files demonstrating various features:
|
|
532
|
+
|
|
533
|
+
1. **`basic-crud.json`** - Simple CRUD operations with templating
|
|
534
|
+
2. **`auth-variants.json`** - Authentication with variants for different scenarios
|
|
535
|
+
3. **`users-faker.json`** - User list with faker-generated data and array ranges
|
|
536
|
+
4. **`companies-nested.json`** - Nested data structures with companies and employees
|
|
537
|
+
5. **`orders-and-matches.json`** - Complex matching with headers, cookies, and query params
|
|
538
|
+
|
|
539
|
+
Run any example:
|
|
540
|
+
```bash
|
|
541
|
+
mockserve serve --spec examples/users-faker.json
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
## Use Cases
|
|
545
|
+
|
|
546
|
+
### Development
|
|
547
|
+
|
|
548
|
+
Replace real backends during frontend development:
|
|
549
|
+
- No backend dependencies
|
|
550
|
+
- Instant API responses
|
|
551
|
+
- Test edge cases easily with variants
|
|
552
|
+
- Simulate network conditions with delays
|
|
553
|
+
|
|
554
|
+
### Testing
|
|
555
|
+
|
|
556
|
+
Create reliable, repeatable test environments:
|
|
557
|
+
- Deterministic data with `fakerSeed`
|
|
558
|
+
- Test error scenarios with `errorRate`
|
|
559
|
+
- Validate request/response flow with history
|
|
560
|
+
- Multiple test scenarios with variants
|
|
561
|
+
|
|
562
|
+
### Documentation
|
|
563
|
+
|
|
564
|
+
Auto-generated interactive API docs:
|
|
565
|
+
- OpenAPI/Swagger UI out of the box
|
|
566
|
+
- View all endpoints and response examples
|
|
567
|
+
- Test endpoints directly in the browser
|
|
568
|
+
- Export OpenAPI spec for tooling
|
|
569
|
+
|
|
570
|
+
### Demos & Prototypes
|
|
571
|
+
|
|
572
|
+
Quickly mock APIs for demos and prototypes:
|
|
573
|
+
- No coding required - just JSON
|
|
574
|
+
- Realistic data with faker
|
|
575
|
+
- Professional-looking APIs
|
|
576
|
+
- Easy to modify and iterate
|
|
577
|
+
|
|
578
|
+
## Schema Validation
|
|
579
|
+
|
|
580
|
+
Your spec file is validated against `mockserve.spec.schema.json`. Use JSON schema validation in your editor (VS Code, WebStorm, etc.) for autocomplete and error checking.
|
|
581
|
+
|
|
582
|
+
Add this to your spec file:
|
|
583
|
+
```json
|
|
584
|
+
{
|
|
585
|
+
"$schema": "./mockserve.spec.schema.json",
|
|
586
|
+
"version": 1,
|
|
587
|
+
...
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
## Development
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
# Install dependencies
|
|
595
|
+
npm install
|
|
596
|
+
|
|
597
|
+
# Run in development mode
|
|
598
|
+
npm run dev
|
|
599
|
+
|
|
600
|
+
# Build
|
|
601
|
+
npm run build
|
|
602
|
+
|
|
603
|
+
# Run tests
|
|
604
|
+
npm test
|
|
605
|
+
|
|
606
|
+
# Watch tests
|
|
607
|
+
npm run test:watch
|
|
608
|
+
|
|
609
|
+
# Generate JSON schema from Zod spec
|
|
610
|
+
npm run schema:build
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
## Architecture
|
|
614
|
+
|
|
615
|
+
- **TypeScript** - Fully typed codebase with strict mode
|
|
616
|
+
- **Fastify** - Fast, low-overhead web framework
|
|
617
|
+
- **Zod** - Runtime schema validation
|
|
618
|
+
- **Faker** - Realistic fake data generation
|
|
619
|
+
- **Pino** - High-performance logging
|
|
620
|
+
- **Vitest** - Fast unit testing
|
|
621
|
+
|
|
622
|
+
## License
|
|
623
|
+
|
|
624
|
+
ISC
|
|
625
|
+
|
|
626
|
+
## Contributing
|
|
627
|
+
|
|
628
|
+
Contributions are welcome! Please ensure:
|
|
629
|
+
- All tests pass (`npm test`)
|
|
630
|
+
- Code is properly typed (no `any`)
|
|
631
|
+
- Functions have JSDoc comments
|
|
632
|
+
- New features include tests and documentation
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
**Made with ❤️ for developers who need reliable mock APIs**
|
package/dist/behavior.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveDelay = resolveDelay;
|
|
4
|
+
exports.sleep = sleep;
|
|
5
|
+
exports.shouldFail = shouldFail;
|
|
6
|
+
exports.resolveBehavior = resolveBehavior;
|
|
7
|
+
const faker_1 = require("@faker-js/faker");
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a delay value from either a number or a range configuration.
|
|
10
|
+
*/
|
|
11
|
+
function resolveDelay(delay) {
|
|
12
|
+
if (!delay)
|
|
13
|
+
return 0;
|
|
14
|
+
if (typeof delay === "number")
|
|
15
|
+
return delay;
|
|
16
|
+
return faker_1.faker.number.int({ min: delay.min, max: delay.max });
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Pause for the given number of milliseconds.
|
|
20
|
+
*/
|
|
21
|
+
function sleep(ms) {
|
|
22
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Decide whether a request should fail based on an error rate.
|
|
26
|
+
*/
|
|
27
|
+
function shouldFail(errorRate) {
|
|
28
|
+
if (errorRate <= 0)
|
|
29
|
+
return false;
|
|
30
|
+
if (errorRate >= 1)
|
|
31
|
+
return true;
|
|
32
|
+
return Math.random() < errorRate;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve behavior settings with precedence: chosen overrides -> endpoint overrides -> global settings.
|
|
36
|
+
*/
|
|
37
|
+
function resolveBehavior(settings, endpointOverrides, chosenOverrides) {
|
|
38
|
+
return {
|
|
39
|
+
delayMs: chosenOverrides?.delayMs ?? endpointOverrides?.delayMs ?? settings.delayMs,
|
|
40
|
+
errorRate: chosenOverrides?.errorRate ?? endpointOverrides?.errorRate ?? settings.errorRate,
|
|
41
|
+
errorStatus: chosenOverrides?.errorStatus ?? endpointOverrides?.errorStatus ?? settings.errorStatus,
|
|
42
|
+
errorResponse: chosenOverrides?.errorResponse ?? endpointOverrides?.errorResponse ?? settings.errorResponse
|
|
43
|
+
};
|
|
44
|
+
}
|