jgloo 1.9.4 → 2.0.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 CHANGED
@@ -1,242 +1,242 @@
1
- <p align="center"><img src="logo.png" alt="jgloo logo" height="120"></p>
2
-
3
- # jgloo
4
-
5
- ![npm](https://img.shields.io/npm/v/jgloo?style=flat-square)
6
- ![npm bundle size](https://img.shields.io/bundlephobia/min/jgloo?style=flat-square)
7
- ![license](https://img.shields.io/npm/l/jgloo?style=flat-square)
8
- [![Issues](https://img.shields.io/github/issues/zosma180/jgloo.svg?style=flat-square)](https://github.com/zosma180/jgloo/issues)
9
-
10
- ---
11
-
12
- ## Description
13
-
14
- **jgloo** is a local HTTP server useful to mock your backend API and speed up the client development.
15
- This project is based on the Node framework Express. The highlights are:
16
-
17
- - Create a ReST API with two rows of code
18
- - Create custom API easily
19
- - Create custom middleware easily (e.g. auth check)
20
- - Store data in accessible JSON files
21
- - Reload the live changes of your mocks automatically (thanks to nodemon package)
22
- - Expose a dedicated folder for the static files (images, assets etc...)
23
- - Support to multipart/form-data requests
24
-
25
- ---
26
-
27
- ## Installation
28
-
29
- ```bash
30
- npm i -D jgloo
31
- ```
32
-
33
- After the installation create a folder "mock" in your project root (you can use another path and folder name if you want).
34
- The **only requirement** is to create a subfolder "api" in your chosen path (e.g. "mock/api").
35
- Now you are ready to [create your first API](#create-a-simple-api).
36
-
37
- ---
38
-
39
- ## Guide
40
-
41
- - [Create a simple API](#create-a-simple-api)
42
- - [Create a default ReST API](#create-a-default-rest-api)
43
- - [Create a custom ReST API](#create-a-custom-rest-api)
44
- - [Create a middleware](#create-a-middleware)
45
- - [Where data are stored](#where-data-are-stored)
46
- - [Expose the static files](#expose-the-static-files)
47
- - [Handle requests with file uploads](#handle-requests-with-file-uploads)
48
- - [Simulate network delay](#simulate-network-delay)
49
- - [Manage path conflicts](#manage-path-conflicts)
50
- - [Run the server](#run-the-server)
51
-
52
- ---
53
-
54
- ### Create a simple API
55
-
56
- To setup your first API create a new file "hello.js" in the "api" folder. The name of the file does not matter. Then insert the following snippet:
57
-
58
- ```javascript
59
- module.exports = {
60
- path: '/hello',
61
- method: 'get',
62
- callback: (req, res) => {
63
- res.json({ message: 'Hello World!' });
64
- },
65
- };
66
- ```
67
-
68
- This code define the GET route http://localhost:3000/hello that returns a JSON with data "{ message: 'Hello World!' }".
69
- You are ready to [run the server](#run-the-server) now.
70
-
71
- ---
72
-
73
- ### Create a default ReST API
74
-
75
- To setup a ReST API, you have to create a new file in the "api" folder with the name you prefer and the following snippet:
76
-
77
- ```javascript
78
- module.exports = {
79
- path: '/user',
80
- method: 'resource',
81
- };
82
- ```
83
-
84
- With these few rows of code will be created 6 routes:
85
-
86
- - GET /user : return the list of users;
87
- - GET /user/:id : return the specific user;
88
- - POST /user : allow to store the full request body as new user and return it as response;
89
- - PUT /user/:id : allow to replace an existent user with the full request body and return it as response;
90
- - PATCH /user/:id : allow to merge an existent user data with the request body values and return it as response;
91
- - DELETE /user/:id : allow to delete an existent user and return the deleted id.
92
-
93
- If you want to skip any of the previous routes, you can add the "not" property:
94
-
95
- ```javascript
96
- module.exports = {
97
- path: '/user',
98
- method: 'resource',
99
- not: ['LIST']
100
- };
101
- ```
102
- The available values of the "not" property are ['LIST', 'READ', 'CREATE', 'UPDATE', 'PATCH', 'DELETE']
103
-
104
- ---
105
-
106
- ### Create a custom ReST API
107
-
108
- If you need to control the logic of your resources, you can create a custom API that read and/or write the data in the JSON database.
109
- To achieve it create a new file in the "api" folder with the name you prefer and the following snippet:
110
-
111
- ```javascript
112
- const { getResource, setResource } = require('jgloo');
113
-
114
- module.exports = {
115
- path: '/user',
116
- method: 'post',
117
- callback: (req, res) => {
118
- // Get the existent resource list or instantiate it
119
- const list = getResource('user') || [];
120
-
121
- // Get the data of the request and add a new field
122
- const user = req.body;
123
- user.extraField = 'value';
124
-
125
- // Push the new model to the list
126
- list.push(user);
127
-
128
- // Store the updated list in the "user.json" file
129
- setResource('user', list);
130
-
131
- // Return the model as JSON
132
- res.json(user);
133
- },
134
- };
135
- ```
136
-
137
- ---
138
-
139
- ### Create a middleware
140
-
141
- To add a middleware, you have to create a folder "middlewares" in your chosen root path (e.g. "mock/middlewares").
142
- Then create a new file inside with the name you prefer and the following sample snippet:
143
-
144
- ```javascript
145
- module.exports = (req, res, next) => {
146
- const isAuthorized = req.get('Authorization') === 'my-token';
147
- isAuthorized ? next() : res.sendStatus(401);
148
- };
149
- ```
150
-
151
- This sample code check for all routes if the "Authorization" header is set and it has the value "my-token".
152
-
153
- ---
154
-
155
- ### Where data are stored
156
-
157
- The resources are stored in JSON files placed in the subfolder "db" of your chosen root path (e.g. "mock/db").
158
- The [default ReST API](#create-a-default-rest-api) store the JSON file with the name generated by resource path replacing the slashes with the minus sign (e.g. "/auth/user" will be stored as "auth-user.json").
159
- If you want to specify the file name of the resources, you can set it as the "name" property of the API:
160
-
161
- ```javascript
162
- module.exports = {
163
- path: '/my/long/path',
164
- method: 'resource',
165
- name: 'user',
166
- };
167
- ```
168
-
169
- With this code, the JSON file will be stored as "user.json".
170
-
171
- ---
172
-
173
- ### Expose the static files
174
-
175
- To expose any static files you have to create the subfolder "static" in your chosen root path (e.g. "mock/static") and put all the resources inside it.
176
- The static content is reachable by "http://localhost:3000/static/...". That's it.
177
-
178
- ---
179
-
180
- ### Handle requests with file uploads
181
-
182
- The multipart/form-data requests are supported by default. The `req.body` will be filled with the right data.
183
- The data of the uploaded files are placed in the `req.files` property and the files are saved in the `static` folder with a temporary name.
184
- It's recommended to add the `static` folder in the `.gitignore` file.
185
-
186
- ---
187
-
188
- ### Simulate network delay
189
-
190
- If you want to simulate a network delay, you can add the `delay` property to your API configuration:
191
-
192
- ```javascript
193
- module.exports = {
194
- ...
195
- delay: 3 // Seconds
196
- };
197
- ```
198
-
199
- ---
200
-
201
- ### Manage path conflicts
202
-
203
- If you have a scenario where two or more paths have conflicting values, e.g.:
204
- - /my-path/:id
205
- - /my-path/my-sub-path
206
-
207
- you can add the `priority` property to your API configuration:
208
-
209
- ```javascript
210
- module.exports = {
211
- ...
212
- priority: 2
213
- };
214
- ```
215
-
216
- The default value is 0. The api with the higher value will be used.
217
-
218
- ---
219
-
220
- ### Run the server
221
-
222
- To run the server execute the following command in your project root:
223
-
224
- ```shell
225
- npx jgloo
226
- ```
227
-
228
- The full optional parameters are:
229
-
230
- ```shell
231
- npx jgloo -f [FOLDER] -p [PORT] -s [STATIC_URL]
232
- ```
233
-
234
- For example:
235
-
236
- ```shell
237
- npx jgloo -f 'mock' -p 3000 -s 'static'
238
- ```
239
-
240
- - "**-f**" or "**--folder**": the folder where your mocks are placed. It's optional, by default it's the folder "mock".
241
- - "**-p**" or "**--port**": the port of running server. It's optional, by default it's "3000".
242
- - "**-s**" or "**--static**": the url prefix of the static resources. It's optional, by default it's "static".
1
+ <p align="center"><img src="logo.png" alt="jgloo logo" height="120"></p>
2
+
3
+ # jgloo
4
+
5
+ ![npm](https://img.shields.io/npm/v/jgloo?style=flat-square)
6
+ ![npm bundle size](https://img.shields.io/bundlephobia/min/jgloo?style=flat-square)
7
+ ![license](https://img.shields.io/npm/l/jgloo?style=flat-square)
8
+ [![Issues](https://img.shields.io/github/issues/zosma180/jgloo.svg?style=flat-square)](https://github.com/zosma180/jgloo/issues)
9
+
10
+ ---
11
+
12
+ ## Description
13
+
14
+ **jgloo** is a local HTTP server useful to mock your backend API and speed up the client development.
15
+ This project is based on the Node framework Express. The highlights are:
16
+
17
+ - Create a ReST API with two rows of code
18
+ - Create custom API easily
19
+ - Create custom middleware easily (e.g. auth check)
20
+ - Store data in accessible JSON files
21
+ - Reload the live changes of your mocks automatically (thanks to nodemon package)
22
+ - Expose a dedicated folder for the static files (images, assets etc...)
23
+ - Support to multipart/form-data requests
24
+
25
+ ---
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ npm i -D jgloo
31
+ ```
32
+
33
+ After the installation create a folder "mock" in your project root (you can use another path and folder name if you want).
34
+ The **only requirement** is to create a subfolder "api" in your chosen path (e.g. "mock/api").
35
+ Now you are ready to [create your first API](#create-a-simple-api).
36
+
37
+ ---
38
+
39
+ ## Guide
40
+
41
+ - [Create a simple API](#create-a-simple-api)
42
+ - [Create a default ReST API](#create-a-default-rest-api)
43
+ - [Create a custom ReST API](#create-a-custom-rest-api)
44
+ - [Create a middleware](#create-a-middleware)
45
+ - [Where data are stored](#where-data-are-stored)
46
+ - [Expose the static files](#expose-the-static-files)
47
+ - [Handle requests with file uploads](#handle-requests-with-file-uploads)
48
+ - [Simulate network delay](#simulate-network-delay)
49
+ - [Manage path conflicts](#manage-path-conflicts)
50
+ - [Run the server](#run-the-server)
51
+
52
+ ---
53
+
54
+ ### Create a simple API
55
+
56
+ To setup your first API create a new file "hello.js" in the "api" folder. The name of the file does not matter. Then insert the following snippet:
57
+
58
+ ```javascript
59
+ export default {
60
+ path: '/hello',
61
+ method: 'get',
62
+ callback: (req, res) => {
63
+ res.json({ message: 'Hello World!' });
64
+ },
65
+ };
66
+ ```
67
+
68
+ This code define the GET route http://localhost:3000/hello that returns a JSON with data "{ message: 'Hello World!' }".
69
+ You are ready to [run the server](#run-the-server) now.
70
+
71
+ ---
72
+
73
+ ### Create a default ReST API
74
+
75
+ To setup a ReST API, you have to create a new file in the "api" folder with the name you prefer and the following snippet:
76
+
77
+ ```javascript
78
+ export default {
79
+ path: '/user',
80
+ method: 'resource',
81
+ };
82
+ ```
83
+
84
+ With these few rows of code will be created 6 routes:
85
+
86
+ - GET /user : return the list of users;
87
+ - GET /user/:id : return the specific user;
88
+ - POST /user : allow to store the full request body as new user and return it as response;
89
+ - PUT /user/:id : allow to replace an existent user with the full request body and return it as response;
90
+ - PATCH /user/:id : allow to merge an existent user data with the request body values and return it as response;
91
+ - DELETE /user/:id : allow to delete an existent user and return the deleted id.
92
+
93
+ If you want to skip any of the previous routes, you can add the "not" property:
94
+
95
+ ```javascript
96
+ export default {
97
+ path: '/user',
98
+ method: 'resource',
99
+ not: ['LIST']
100
+ };
101
+ ```
102
+ The available values of the "not" property are ['LIST', 'READ', 'CREATE', 'UPDATE', 'PATCH', 'DELETE']
103
+
104
+ ---
105
+
106
+ ### Create a custom ReST API
107
+
108
+ If you need to control the logic of your resources, you can create a custom API that read and/or write the data in the JSON database.
109
+ To achieve it create a new file in the "api" folder with the name you prefer and the following snippet:
110
+
111
+ ```javascript
112
+ import { getResource, setResource } from 'jgloo';
113
+
114
+ export default {
115
+ path: '/user',
116
+ method: 'post',
117
+ callback: (req, res) => {
118
+ // Get the existent resource list or instantiate it
119
+ const list = getResource('user') || [];
120
+
121
+ // Get the data of the request and add a new field
122
+ const user = req.body;
123
+ user.extraField = 'value';
124
+
125
+ // Push the new model to the list
126
+ list.push(user);
127
+
128
+ // Store the updated list in the "user.json" file
129
+ setResource('user', list);
130
+
131
+ // Return the model as JSON
132
+ res.json(user);
133
+ },
134
+ };
135
+ ```
136
+
137
+ ---
138
+
139
+ ### Create a middleware
140
+
141
+ To add a middleware, you have to create a folder "middlewares" in your chosen root path (e.g. "mock/middlewares").
142
+ Then create a new file inside with the name you prefer and the following sample snippet:
143
+
144
+ ```javascript
145
+ export default function(req, res, next) {
146
+ const isAuthorized = req.get('Authorization') === 'my-token';
147
+ isAuthorized ? next() : res.sendStatus(401);
148
+ };
149
+ ```
150
+
151
+ This sample code check for all routes if the "Authorization" header is set and it has the value "my-token".
152
+
153
+ ---
154
+
155
+ ### Where data are stored
156
+
157
+ The resources are stored in JSON files placed in the subfolder "db" of your chosen root path (e.g. "mock/db").
158
+ The [default ReST API](#create-a-default-rest-api) store the JSON file with the name generated by resource path replacing the slashes with the minus sign (e.g. "/auth/user" will be stored as "auth-user.json").
159
+ If you want to specify the file name of the resources, you can set it as the "name" property of the API:
160
+
161
+ ```javascript
162
+ export default {
163
+ path: '/my/long/path',
164
+ method: 'resource',
165
+ name: 'user',
166
+ };
167
+ ```
168
+
169
+ With this code, the JSON file will be stored as "user.json".
170
+
171
+ ---
172
+
173
+ ### Expose the static files
174
+
175
+ To expose any static files you have to create the subfolder "static" in your chosen root path (e.g. "mock/static") and put all the resources inside it.
176
+ The static content is reachable by "http://localhost:3000/static/...". That's it.
177
+
178
+ ---
179
+
180
+ ### Handle requests with file uploads
181
+
182
+ The multipart/form-data requests are supported by default. The `req.body` will be filled with the right data.
183
+ The data of the uploaded files are placed in the `req.files` property and the files are saved in the `static` folder with a temporary name.
184
+ It's recommended to add the `static` folder in the `.gitignore` file.
185
+
186
+ ---
187
+
188
+ ### Simulate network delay
189
+
190
+ If you want to simulate a network delay, you can add the `delay` property to your API configuration:
191
+
192
+ ```javascript
193
+ export default {
194
+ ...
195
+ delay: 3 // Seconds
196
+ };
197
+ ```
198
+
199
+ ---
200
+
201
+ ### Manage path conflicts
202
+
203
+ If you have a scenario where two or more paths have conflicting values, e.g.:
204
+ - /my-path/:id
205
+ - /my-path/my-sub-path
206
+
207
+ you can add the `priority` property to your API configuration:
208
+
209
+ ```javascript
210
+ export default {
211
+ ...
212
+ priority: 2
213
+ };
214
+ ```
215
+
216
+ The default value is 0. The api with the higher value will be used.
217
+
218
+ ---
219
+
220
+ ### Run the server
221
+
222
+ To run the server execute the following command in your project root:
223
+
224
+ ```shell
225
+ npx jgloo
226
+ ```
227
+
228
+ The full optional parameters are:
229
+
230
+ ```shell
231
+ npx jgloo -f [FOLDER] -p [PORT] -s [STATIC_URL]
232
+ ```
233
+
234
+ For example:
235
+
236
+ ```shell
237
+ npx jgloo -f 'mock' -p 3000 -s 'static'
238
+ ```
239
+
240
+ - "**-f**" or "**--folder**": the folder where your mocks are placed. It's optional, by default it's the folder "mock".
241
+ - "**-p**" or "**--port**": the port of running server. It's optional, by default it's "3000".
242
+ - "**-s**" or "**--static**": the url prefix of the static resources. It's optional, by default it's "static".
package/cli.mjs ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';'use strict';
4
+ import { execSync } from 'child_process';
5
+ import minimist from 'minimist';
6
+ import { dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const pkg = dirname(fileURLToPath(import.meta.url));
10
+ const params = minimist(process.argv.slice(2));
11
+ const folder = params.f || params.folder || 'mock';
12
+ const port = params.p || params.port || 3000;
13
+ const staticUrl = params.s || params.static || 'static';
14
+
15
+ const nodemon = 'nodemon --ignore "db/*.json"';
16
+ const target = `"${pkg}/server.mjs" "${folder}" "${port}" "${staticUrl}"`;
17
+ execSync(`${nodemon} --watch "${folder}" ${target}`, { stdio: 'inherit' });
package/jgloo.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare function getResource<T>(name: string): T;
1
+ export declare function getResource<T>(name: string): T;
2
2
  export declare function setResource<T>(name: string, value: T): void;
package/jgloo.mjs ADDED
@@ -0,0 +1,28 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+
3
+ const params = process.argv.slice(2);
4
+ const db = `${params[0]}/db`;
5
+
6
+ export const getResource = name => {
7
+ const path = `${db}/${name}.json`;
8
+ if (!existsSync(path)) { return null; }
9
+ const file = readFileSync(path);
10
+
11
+ try {
12
+ return JSON.parse(file);
13
+ } catch (error) {
14
+ throw new Error(`${file}.json is an invalid JSON.`);
15
+ }
16
+ }
17
+
18
+ export const setResource = (name, value) => {
19
+ if (!existsSync(db)) { mkdirSync(db); }
20
+ const path = `${db}/${name}.json`;
21
+ writeFileSync(path, JSON.stringify(value), 'utf8');
22
+ };
23
+
24
+ export const getDelayMiddleware = delay => {
25
+ return (_, __, next) => {
26
+ setTimeout(next, delay * 1000);
27
+ };
28
+ }
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "jgloo",
3
- "version": "1.9.4",
3
+ "version": "2.0.0",
4
4
  "description": "The coldest mock server.",
5
5
  "homepage": "https://github.com/zosma180/jgloo",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/zosma180/jgloo.git"
9
9
  },
10
- "main": "jgloo.js",
10
+ "main": "jgloo.mjs",
11
11
  "scripts": {
12
- "start": "./cli",
12
+ "start": "node cli.mjs",
13
13
  "deploy": "npm-deploy . ."
14
14
  },
15
15
  "bin": {
16
- "jgloo": "cli"
16
+ "jgloo": "cli.mjs"
17
17
  },
18
18
  "keywords": [
19
19
  "jgloo",
package/rest.mjs ADDED
@@ -0,0 +1,93 @@
1
+ import path from 'path';
2
+ import { getResource, setResource } from './jgloo.mjs';
3
+
4
+ export const configureResource = (app, config, middleware) => {
5
+ const fallback = config.path.split('/').filter(Boolean).join('-');
6
+ const resourceName = config.name || fallback;
7
+ const skips = config.not || [];
8
+
9
+ // List
10
+ if (skips.includes('LIST') === false) {
11
+ app.get(config.path, middleware, (req, res) => {
12
+ const resource = getResource(resourceName) || [];
13
+ res.json(resource);
14
+ });
15
+ }
16
+
17
+ // Read
18
+ if (skips.includes('READ') === false) {
19
+ app.get(`${config.path}/:id`, middleware, (req, res) => {
20
+ const id = Number(req.params.id);
21
+
22
+ const resource = getResource(resourceName) || [];
23
+ const model = resource.find(r => r.id === id);
24
+ if (!model) { return res.sendStatus(404); }
25
+
26
+ res.json(model);
27
+ });
28
+ }
29
+
30
+ // Create
31
+ if (skips.includes('CREATE') === false) {
32
+ app.post(config.path, middleware, (req, res) => {
33
+ const resource = getResource(resourceName) || [];
34
+ const model = req.body;
35
+
36
+ model.id = Date.now();
37
+ resource.push(model);
38
+ setResource(resourceName, resource);
39
+
40
+ res.json(model);
41
+ });
42
+ }
43
+
44
+ // Update
45
+ if (skips.includes('UPDATE') === false) {
46
+ app.put(`${config.path}/:id`, middleware, (req, res) => {
47
+ const id = Number(req.params.id);
48
+
49
+ const resource = getResource(resourceName) || [];
50
+ const index = resource.findIndex(r => r.id === id);
51
+ if (index === -1) { return res.sendStatus(404); }
52
+
53
+ resource[index] = req.body;
54
+ resource[index].id = id;
55
+ setResource(resourceName, resource);
56
+
57
+ res.json(resource[index]);
58
+ });
59
+ }
60
+
61
+ // Patch
62
+ if (skips.includes('PATCH') === false) {
63
+ app.patch(`${config.path}/:id`, middleware, (req, res) => {
64
+ const id = Number(req.params.id);
65
+
66
+ const resource = getResource(resourceName) || [];
67
+ const index = resource.findIndex(r => r.id === id);
68
+ if (index === -1) { return res.sendStatus(404); }
69
+
70
+ resource[index] = { ...resource[index], ...req.body };
71
+ resource[index].id = id;
72
+ setResource(resourceName, resource);
73
+
74
+ res.json(resource[index]);
75
+ });
76
+ }
77
+
78
+ // Delete
79
+ if (skips.includes('DELETE') === false) {
80
+ app.delete(`${config.path}/:id`, middleware, (req, res) => {
81
+ const id = Number(req.params.id);
82
+
83
+ let resource = getResource(resourceName) || [];
84
+ const index = resource.findIndex(r => r.id === id);
85
+ if (index === -1) { return res.sendStatus(404); }
86
+
87
+ resource = resource.filter(r => r.id !== id);
88
+ setResource(resourceName, resource);
89
+
90
+ res.json(id);
91
+ });
92
+ }
93
+ }