view-api 1.0.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/README.md +58 -0
- package/bin/mock-runner.js +20 -0
- package/package.json +26 -0
- package/src/app.js +29 -0
- package/src/controllers/mock.controller.js +21 -0
- package/src/mocks/mock.json +51 -0
- package/src/routes/mock.routes.js +8 -0
- package/src/server.js +10 -0
- package/src/services/mock.service.js +15 -0
- package/src/utils/loadConfig.js +6 -0
- package/src/utils/responsePicker.js +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# view-api
|
|
2
|
+
|
|
3
|
+
A lightweight CLI tool to run mock APIs locally from a JSON configuration file.
|
|
4
|
+
|
|
5
|
+
Perfect for frontend development, testing, and prototyping without a real backend.
|
|
6
|
+
|
|
7
|
+
## β¨ Features
|
|
8
|
+
|
|
9
|
+
- Run mock APIs from a single JSON file
|
|
10
|
+
- Customizable port
|
|
11
|
+
- Randomized success / error responses
|
|
12
|
+
- Zero setup for frontend teams
|
|
13
|
+
- Works fully offline
|
|
14
|
+
|
|
15
|
+
## π¦ Installation
|
|
16
|
+
|
|
17
|
+
You donβt need to install it globally.
|
|
18
|
+
|
|
19
|
+
Run directly with `npx`:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx watch-api start ./mock.json --port 4000
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## π Mock Config Format
|
|
26
|
+
|
|
27
|
+
You gotta use this json structure in able to use the api
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"version": "1.0",
|
|
32
|
+
"routes": {
|
|
33
|
+
"GET /products": {
|
|
34
|
+
"behavior": {
|
|
35
|
+
"successRate": 70
|
|
36
|
+
},
|
|
37
|
+
"responses": {
|
|
38
|
+
"success": {
|
|
39
|
+
"statusCode": 200,
|
|
40
|
+
"body": {
|
|
41
|
+
"status": "success",
|
|
42
|
+
"data": [{ "id": 1, "name": "Product A" }]
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"errors": [
|
|
46
|
+
{
|
|
47
|
+
"statusCode": 500,
|
|
48
|
+
"body": {
|
|
49
|
+
"status": "failed",
|
|
50
|
+
"message": "Server error"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { program } from "commander";
|
|
5
|
+
import startServer from "../src/server.js";
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.name("mock-runner")
|
|
9
|
+
.description("Run mock APIs from a JSON config")
|
|
10
|
+
.version("1.0.0");
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.command("start <config>")
|
|
14
|
+
.option("-p, --port <port>", "port to run server", "3000")
|
|
15
|
+
.action((config, options) => {
|
|
16
|
+
const configPath = path.resolve(process.cwd(), config);
|
|
17
|
+
startServer({ configPath, port: Number(options.port) || 8734 });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "view-api",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"server": "nodemon src/server.js",
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"mock-runner": "./bin/mock-runner.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"commander": "^14.0.2",
|
|
19
|
+
"cors": "^2.8.5",
|
|
20
|
+
"dotenv": "^17.2.3",
|
|
21
|
+
"express": "^5.2.1",
|
|
22
|
+
"helmet": "^8.1.0",
|
|
23
|
+
"morgan": "^1.10.1",
|
|
24
|
+
"nodemon": "^3.1.11"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/app.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import morgan from "morgan";
|
|
3
|
+
import helmet from "helmet";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
|
|
6
|
+
// define routes here
|
|
7
|
+
import mockRoutes from "./routes/mock.routes.js";
|
|
8
|
+
|
|
9
|
+
import "dotenv/config";
|
|
10
|
+
|
|
11
|
+
export default function createApp(configPath) {
|
|
12
|
+
const app = express();
|
|
13
|
+
|
|
14
|
+
// ==== MIDDLEWARES ====
|
|
15
|
+
app.use(express.json());
|
|
16
|
+
app.use(express.urlencoded({ extended: true }));
|
|
17
|
+
app.use(morgan("dev"));
|
|
18
|
+
app.use(cors());
|
|
19
|
+
app.use(helmet());
|
|
20
|
+
|
|
21
|
+
app.use((req, _res, next) => {
|
|
22
|
+
req.mockConfigPath = configPath;
|
|
23
|
+
next();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
app.use(mockRoutes);
|
|
27
|
+
|
|
28
|
+
return app;
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { handle } from "../services/mock.service.js";
|
|
2
|
+
|
|
3
|
+
export const handleRequest = async (req, res, next) => {
|
|
4
|
+
try {
|
|
5
|
+
// call service to handle request
|
|
6
|
+
const data = handle(req.mockConfigPath, req.method, req.path);
|
|
7
|
+
console.log("π ~ handleRequest ~ data:", data);
|
|
8
|
+
|
|
9
|
+
if (!data) {
|
|
10
|
+
return res.status(404).json({
|
|
11
|
+
status: "failed",
|
|
12
|
+
message: `No mock defined for ${req.method} ${req.path}`,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// return response
|
|
17
|
+
return res.status(data.statusCode).json(data.body);
|
|
18
|
+
} catch (err) {
|
|
19
|
+
next(err);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"routes": {
|
|
4
|
+
"GET /products": {
|
|
5
|
+
"behavior": {
|
|
6
|
+
"successRate": 50
|
|
7
|
+
},
|
|
8
|
+
"responses": {
|
|
9
|
+
"success": {
|
|
10
|
+
"statusCode": 200,
|
|
11
|
+
"body": {
|
|
12
|
+
"status": "success",
|
|
13
|
+
"message": "Products fetched wkwk",
|
|
14
|
+
"data": [
|
|
15
|
+
{
|
|
16
|
+
"id": 1,
|
|
17
|
+
"name": "Product A",
|
|
18
|
+
"price": 10000,
|
|
19
|
+
"stock": 50
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": 2,
|
|
23
|
+
"name": "Product B",
|
|
24
|
+
"price": 15000,
|
|
25
|
+
"stock": 30
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"errors": [
|
|
31
|
+
{
|
|
32
|
+
"statusCode": 500,
|
|
33
|
+
"body": {
|
|
34
|
+
"status": "failed",
|
|
35
|
+
"message": "Server error",
|
|
36
|
+
"error_code": "SERVER_ERROR"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"statusCode": 400,
|
|
41
|
+
"body": {
|
|
42
|
+
"status": "failed",
|
|
43
|
+
"message": "Bad request, invalid parameters",
|
|
44
|
+
"error_code": "INVALID_PARAMETERS"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/server.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import createApp from "./app.js";
|
|
2
|
+
|
|
3
|
+
export default ({ configPath, port }) => {
|
|
4
|
+
const server = createApp(configPath);
|
|
5
|
+
|
|
6
|
+
server.listen(port, () => {
|
|
7
|
+
console.log(` β [API] Server running on: http://localhost:${port}`);
|
|
8
|
+
console.log(` β [API] Using config file: ${configPath}`);
|
|
9
|
+
});
|
|
10
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { loadConfig } from "../utils/loadConfig.js";
|
|
2
|
+
import { pickResponse } from "../utils/responsePicker.js";
|
|
3
|
+
|
|
4
|
+
export const handle = (mockConfigPath, method, path) => {
|
|
5
|
+
const config = loadConfig(mockConfigPath);
|
|
6
|
+
const key = `${method} ${path}`;
|
|
7
|
+
|
|
8
|
+
const route = config.routes[key];
|
|
9
|
+
|
|
10
|
+
if (!route) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return pickResponse(route);
|
|
15
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const pickResponse = (routeConfig) => {
|
|
2
|
+
const { behavior, responses } = routeConfig;
|
|
3
|
+
const roll = Math.random() * 100;
|
|
4
|
+
|
|
5
|
+
if (roll <= behavior.successRate) {
|
|
6
|
+
return {
|
|
7
|
+
statusCode: responses.success.statusCode,
|
|
8
|
+
body: responses.success.body,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const errors = responses.errors;
|
|
13
|
+
const error = errors[Math.floor(Math.random() * errors.length)];
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
statusCode: error.statusCode,
|
|
17
|
+
body: error.body,
|
|
18
|
+
};
|
|
19
|
+
};
|