just-another-http-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/LICENSE +21 -0
- package/README.md +233 -0
- package/api.js +166 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Oliver Edgington
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
[](https://github.com/OllieEdge/just-another-http-api/actions/workflows/main.yml)
|
|
4
|
+
[](https://github.com/Naereen/badges/)
|
|
5
|
+
[](https://github.com/Naereen/StrapDown.js/blob/master/LICENSE)
|
|
6
|
+
[](https://github.com/Naereen/badges)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Just Another HTTP API
|
|
10
|
+
A framework built on top of restify aimed at removing the need for any network or server configuration. You can install this package and immediately begin coding logic for your endpoints.
|
|
11
|
+
|
|
12
|
+
This framework scans your `./routes` directory (*configurable*) and automatically builds all of your endpoints using the file and folder structure you setup. For each endpoint a single `*.js` will be able to split logic based on the method of the requests.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
- Install package: `npm i just-another-http-api`
|
|
17
|
+
- Make routes directory: `mkdir routes`
|
|
18
|
+
- Add the following to your app.js:
|
|
19
|
+
```
|
|
20
|
+
const API = require('just-another-http-api');
|
|
21
|
+
|
|
22
|
+
const server = await API();
|
|
23
|
+
// server is now ready.
|
|
24
|
+
```
|
|
25
|
+
- Add a route: `touch ./routes/endpoint.js`
|
|
26
|
+
- Handle request in your new endpoint:
|
|
27
|
+
```
|
|
28
|
+
exports.get = async req => {
|
|
29
|
+
return {
|
|
30
|
+
"hello": "world"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
- Run server: `npm start`
|
|
35
|
+
- Load http://localhost:4001/endpoint in your browser and you will see the JSON response.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Supported Node Versions
|
|
39
|
+
This is a personal project I've modified a few times over the years, I've just cleaned everything up and made OpenSource, in the process I've made it compatible with the latest Node as of May 2022 (v18), previous node versions are not supported.
|
|
40
|
+
|
|
41
|
+
| Node Release | Supported in Current Version | Notes |
|
|
42
|
+
| :--: | :---: | :---: |
|
|
43
|
+
| 18.x | **Yes** | Current stable |
|
|
44
|
+
| <18 | **No** | No Support |
|
|
45
|
+
|
|
46
|
+
# Usage
|
|
47
|
+
## Initialise server
|
|
48
|
+
Pretty simple to get the server running, use one of the following options:
|
|
49
|
+
```
|
|
50
|
+
const API = require ( 'just-another-http-api' );
|
|
51
|
+
|
|
52
|
+
// Option 1
|
|
53
|
+
const server = await API();
|
|
54
|
+
|
|
55
|
+
// Option 2 - you should use this option.Find more about config settings below.
|
|
56
|
+
const config = {
|
|
57
|
+
docRoot: 'customDir',
|
|
58
|
+
bodyParser: true,
|
|
59
|
+
queryParser: true
|
|
60
|
+
}
|
|
61
|
+
const server = await API( config );
|
|
62
|
+
|
|
63
|
+
// Option 3
|
|
64
|
+
const restify = require ( 'restify' );
|
|
65
|
+
const config = {
|
|
66
|
+
docRoot: 'customDir',
|
|
67
|
+
bodyParser: true,
|
|
68
|
+
queryParser: true
|
|
69
|
+
}
|
|
70
|
+
const reportAnalytics = ( endpointUsage => { console.log ( endpointUsage.path ) } );
|
|
71
|
+
const customServer = restify.createServer();
|
|
72
|
+
const server = await API( config, reportAnalytics, customServer );
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If you need to shut the server down, make sure you have stored the server instance and call its `close()` method. eg:
|
|
76
|
+
```
|
|
77
|
+
server.close();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Set up your endpoints
|
|
81
|
+
Create routes using your file and folder structure. By default Just Another HTTP API will look for a routes folder in your working directory, you can change this by parsing a docRoot in the config.
|
|
82
|
+
```
|
|
83
|
+
// Config Object
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
docRoot: 'customFolder'
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
If your folder structure was the following:
|
|
91
|
+
```
|
|
92
|
+
root/
|
|
93
|
+
├── app.js
|
|
94
|
+
├── routes/
|
|
95
|
+
│ ├── index.js
|
|
96
|
+
│ ├── users/
|
|
97
|
+
│ │ ├── index.js
|
|
98
|
+
│ │ ├── userId.js
|
|
99
|
+
│ └── atoms/
|
|
100
|
+
│ ├── index.js
|
|
101
|
+
│ ├── atomId.js
|
|
102
|
+
│ ├── protons/
|
|
103
|
+
│ │ └── protonId.js
|
|
104
|
+
│ ├── neutrons/
|
|
105
|
+
│ │ └── neutronId.js
|
|
106
|
+
│ └── electrons/
|
|
107
|
+
│ └── electronId.js
|
|
108
|
+
└── package.json
|
|
109
|
+
```
|
|
110
|
+
It would give you the following endpoints:
|
|
111
|
+
```
|
|
112
|
+
Endpoint File used
|
|
113
|
+
-------- ---------
|
|
114
|
+
... (routes/index.js)
|
|
115
|
+
.../users (routes/users/index.js)
|
|
116
|
+
.../users/:userId (routes/users/userId.js)
|
|
117
|
+
.../atoms (routes/atoms/index.js)
|
|
118
|
+
.../atoms/:atomId (routes/atoms/atomId.js)
|
|
119
|
+
.../atoms/protons/:protonId (routes/atoms/protons/protonId.js)
|
|
120
|
+
.../atoms/neutrons/:neutronId (routes/atoms/neutrons/neutronId.js)
|
|
121
|
+
.../atoms/electrons/:electronsId (routes/atoms/electrons/electronsId.js)
|
|
122
|
+
```
|
|
123
|
+
---
|
|
124
|
+
### Configuring your endpoint logic
|
|
125
|
+
Each endpoint can export any of the follow http methods: `head, get, post, put, patch, del`
|
|
126
|
+
|
|
127
|
+
Just Another HTTP API doesn't really care about the method you are using, there is not logical difference so it will be up to you to decide how you implement them. Typically HEAD and GET never changes anything it only returns information, whereas PUT, POST, PATCH and DEL will most likely modify some kind of data. More about HTTP Methods can be found here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
|
128
|
+
|
|
129
|
+
For this example we are going to be using a file located at `./routes/users/userId.js` in our project. It will accept GET and POST methods. Here is the code for this file:
|
|
130
|
+
```
|
|
131
|
+
exports.get = async req => {
|
|
132
|
+
|
|
133
|
+
// Because this filename has 'Id' in its title it will extract the userId provided in the request and make it avaiable via the request parameters
|
|
134
|
+
const { userId } = req.params;
|
|
135
|
+
|
|
136
|
+
// Connect to a datastore and retrieve user data using the userId provided in the params.
|
|
137
|
+
const user = await db.query(`SELECT * FROM users WHERE id = ${ userId }`);
|
|
138
|
+
|
|
139
|
+
// Return the data in the response
|
|
140
|
+
return user;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
exports.post = async req => {
|
|
144
|
+
|
|
145
|
+
// extract the JSON we sent in the request. Requires config.bodyParser to be true
|
|
146
|
+
const { body } = req;
|
|
147
|
+
const { userId } = req.params;
|
|
148
|
+
|
|
149
|
+
// Update a user login goes here
|
|
150
|
+
const user = {};
|
|
151
|
+
|
|
152
|
+
return user;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
exports.put = async req => { throw new Error ( 'Not configured' ) };
|
|
156
|
+
exports.del = async req => { throw new Error ( 'Not configured' ) };
|
|
157
|
+
exports.patch = async req => { throw new Error ( 'Not configured' ) };
|
|
158
|
+
```
|
|
159
|
+
---
|
|
160
|
+
## Example configuration
|
|
161
|
+
When initialising the API you can parse it a config object. Below is a typical variant:
|
|
162
|
+
```
|
|
163
|
+
{
|
|
164
|
+
bodyParser: true,
|
|
165
|
+
queryParser: true,
|
|
166
|
+
uploads: {
|
|
167
|
+
enabled: true
|
|
168
|
+
},
|
|
169
|
+
docRoot: './routes',
|
|
170
|
+
port: 4001,
|
|
171
|
+
cors: {
|
|
172
|
+
credentials: false,
|
|
173
|
+
origins: [ '*' ],
|
|
174
|
+
allowHeaders: [
|
|
175
|
+
'accept',
|
|
176
|
+
'accept-version',
|
|
177
|
+
'content-type',
|
|
178
|
+
'request-id',
|
|
179
|
+
'origin',
|
|
180
|
+
'x-api-version',
|
|
181
|
+
'x-request-id'
|
|
182
|
+
],
|
|
183
|
+
exposeHeaders: [
|
|
184
|
+
'accept',
|
|
185
|
+
'accept-version',
|
|
186
|
+
'content-type',
|
|
187
|
+
'request-id',
|
|
188
|
+
'origin',
|
|
189
|
+
'x-api-version',
|
|
190
|
+
'x-request-id'
|
|
191
|
+
],
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
| Config option | Default | Type | Description | Notes |
|
|
197
|
+
| :--: | :--: | :--: | :---: | :---: |
|
|
198
|
+
| bodyParser | `false` | Boolean | Will enable the `.body` attribute of the `request` object | -
|
|
199
|
+
| queryParser | `false` | Boolean | Will enable the `.query` attribute of the `request` object | Query parameters will be stored in a key/value object
|
|
200
|
+
| uploads | `null` | Object | Enables the `application/octet-stream` content-type | Supported by the multer module, see more here for config options: https://github.com/expressjs/multer
|
|
201
|
+
| docRoot | `./routes` | String | Changes the directory to map endpoints with | -
|
|
202
|
+
| port | `4001` | Integer | Set the port number the server runs on | -
|
|
203
|
+
| cors | `null` | Object | Set the default cors | -
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
## LICENSE
|
|
207
|
+
MIT License
|
|
208
|
+
|
|
209
|
+
Copyright (c) 2022 Oliver Edgington
|
|
210
|
+
|
|
211
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
212
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
213
|
+
in the Software without restriction, including without limitation the rights
|
|
214
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
215
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
216
|
+
furnished to do so, subject to the following conditions:
|
|
217
|
+
|
|
218
|
+
The above copyright notice and this permission notice shall be included in all
|
|
219
|
+
copies or substantial portions of the Software.
|
|
220
|
+
|
|
221
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
222
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
223
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
224
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
225
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
226
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
227
|
+
SOFTWARE.
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
## Contact & Links
|
|
231
|
+
Oliver Edgington <oliver@edgington.com>
|
|
232
|
+
|
|
233
|
+
[](https://twitter.com/OllieEdge)
|
package/api.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const restify = require ( 'restify' );
|
|
2
|
+
const restifyErrors = require ( 'restify-errors' );
|
|
3
|
+
const corsPlugin = require ( 'restify-cors-middleware2' );
|
|
4
|
+
const recursiveRead = require ( 'recursive-readdir' );
|
|
5
|
+
const packageJson = require ( './package.json' );
|
|
6
|
+
const path = require ( 'path' );
|
|
7
|
+
const multer = require ( 'multer' );
|
|
8
|
+
const storage = multer.memoryStorage ();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* See README for config setup.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} config The config, see readme for example configurations
|
|
14
|
+
* @param {Function} every An optional agrument that accepts a function ready to receive an object. The function will be called everytime a endpoint is requested (good for analytical usage)
|
|
15
|
+
* @param {RestifyServer} _server Optionally override the restify instance in this API and use your own. Accepts a `restify.createServer()` instance.
|
|
16
|
+
*/
|
|
17
|
+
module.exports = async ( config, every = null, _server = null ) => {
|
|
18
|
+
|
|
19
|
+
if ( !config ) console.log ( 'JustAnother: WARNING: You\'ve initialised Just Another Http API without any config. This is not recommended.' );
|
|
20
|
+
|
|
21
|
+
let upload;
|
|
22
|
+
let server = _server;
|
|
23
|
+
|
|
24
|
+
if ( _server ) console.debug ( 'JustAnother: Using restify override instance provided.' );
|
|
25
|
+
else {
|
|
26
|
+
server = restify.createServer ( {
|
|
27
|
+
name: packageJson.name,
|
|
28
|
+
version: packageJson.version
|
|
29
|
+
} );
|
|
30
|
+
|
|
31
|
+
if ( config?.bodyParser ) server.use ( restify.plugins.queryParser () );
|
|
32
|
+
if ( config?.bodyParser ) server.use ( restify.plugins.bodyParser () );
|
|
33
|
+
if ( config?.uploads && config?.uploads.enabled ) upload = multer ( { storage: storage } );
|
|
34
|
+
|
|
35
|
+
if ( config?.cors ){
|
|
36
|
+
const cors = corsPlugin ( config.cors );
|
|
37
|
+
server.pre ( cors.preflight );
|
|
38
|
+
server.use ( cors.actual );
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
server.on ( 'MethodNotAllowed', unknownMethodHandler );
|
|
43
|
+
|
|
44
|
+
const files = await recursiveReadDir ( config?.docRoot || 'routes' );
|
|
45
|
+
const endpoints = files.map ( ( filePath ) => ( {
|
|
46
|
+
handlers: require ( path.resolve ( filePath ) ),
|
|
47
|
+
path: handlerPathToApiPath ( filePath, config?.docRoot || 'routes' )
|
|
48
|
+
} ) );
|
|
49
|
+
|
|
50
|
+
endpoints.forEach ( endpoint => {
|
|
51
|
+
Object.keys ( endpoint.handlers ).forEach ( method => {
|
|
52
|
+
const endpointArgs = [
|
|
53
|
+
endpoint.path,
|
|
54
|
+
method === 'post' && upload ? upload.single ( 'file' ) : null
|
|
55
|
+
].filter ( Boolean );
|
|
56
|
+
|
|
57
|
+
server[ method ] ( ...endpointArgs, async ( req, res ) => {
|
|
58
|
+
try {
|
|
59
|
+
const response = await endpoint.handlers[ method ] ( req );
|
|
60
|
+
|
|
61
|
+
if ( every ) {
|
|
62
|
+
every ( { path: endpoint.path, method, req } );
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If optional headers have been provided in the response add them here.
|
|
66
|
+
if ( response.hasOwnProperty ( 'headers' ) ){
|
|
67
|
+
res.set ( response.headers );
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// If response.html is set, we want to send the HTML back as a raw string and set the content type.
|
|
71
|
+
if ( response.hasOwnProperty ( 'html' ) ){
|
|
72
|
+
res.sendRaw ( 200, response.html, { 'Content-Type': 'text/html' } );
|
|
73
|
+
} //
|
|
74
|
+
else if ( response.hasOwnProperty ( 'json' ) || response.hasOwnProperty ( 'body' ) || response.hasOwnProperty ( 'response' ) || typeof response === 'string' ){
|
|
75
|
+
data = response?.json || response?.body || response?.response || response;
|
|
76
|
+
res.send ( method === 'post' ? 201 : 200, data );
|
|
77
|
+
}
|
|
78
|
+
else if ( response.hasOwnProperty ( 'error' ) ){
|
|
79
|
+
res.send ( new restifyErrors.makeErrFromCode ( response?.error?.statusCode, response?.error?.message ) );
|
|
80
|
+
}
|
|
81
|
+
else if ( response.hasOwnProperty ( 'file' ) ){
|
|
82
|
+
res.sendRaw ( response.file );
|
|
83
|
+
}
|
|
84
|
+
else if ( typeof response === 'object' ){
|
|
85
|
+
res.send ( response ); //Try and send whatever it is
|
|
86
|
+
}
|
|
87
|
+
else if ( !response ){
|
|
88
|
+
res.send ( 204 );
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
res.send ( new restifyErrors.makeErrFromCode ( 500, `Just Another Http API did not understand the response provided for request: ${ method } to ${ endpoint.path }. Check your return value.` ) );
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
catch ( error ){
|
|
97
|
+
if ( error instanceof Error ) {
|
|
98
|
+
res.send ( new restifyErrors.InternalServerError ( { code: 500 }, error.stack.replace ( /\n/g, ' ' ) ) );
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
if ( error.code ) {
|
|
102
|
+
res.send ( new restifyErrors.makeErrFromCode ( error.code, error.message ) );
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
res.send ( new restifyErrors.InternalServerError ( { code: 500 }, JSON.stringify ( error, null, 2 ) ) );
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
} );
|
|
111
|
+
} );
|
|
112
|
+
} );
|
|
113
|
+
|
|
114
|
+
await server.listen ( process.env.PORT || config?.port || 4001 );
|
|
115
|
+
|
|
116
|
+
return server;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handlerPathToApiPath = ( path, docRoot ) => {
|
|
120
|
+
|
|
121
|
+
const targetPath = path.replace ( docRoot.replace ( /.\//igm, '' ), '' ).split ( '/' );
|
|
122
|
+
|
|
123
|
+
return '/' + targetPath.map ( subPath => {
|
|
124
|
+
if ( subPath.includes ( 'index.js' ) && path.substr ( path.lastIndexOf ( '/' ) ).includes ( 'index' ) ) return null;
|
|
125
|
+
if ( ( subPath.includes ( '-' ) || subPath.includes ( 'Id' ) ) && ( subPath.includes ( '.js' ) || subPath.includes ( 'Id' ) ) ) {
|
|
126
|
+
subPath = subPath.split ( '-' ).map ( urlParameter => {
|
|
127
|
+
urlParameter = urlParameter.replace ( /-/igm, '' );
|
|
128
|
+
|
|
129
|
+
return ':' + urlParameter;
|
|
130
|
+
} ).join ( '/' );
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return subPath.replace ( /.js/igm, '' );
|
|
134
|
+
} ).filter ( Boolean ).join ( '/' );
|
|
135
|
+
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const recursiveReadDir = async ( docRoot ) => {
|
|
139
|
+
try {
|
|
140
|
+
const files = await recursiveRead ( docRoot );
|
|
141
|
+
|
|
142
|
+
// Remove all falsy values and reverse the array.
|
|
143
|
+
return files.filter ( filePath => filePath ? !filePath.includes ( 'DS_Store' ) : false ).reverse ();
|
|
144
|
+
}
|
|
145
|
+
catch ( e ){
|
|
146
|
+
console.error ( 'JustAnother: Failed to load your routes directory for generating endpoints.' );
|
|
147
|
+
throw e;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const unknownMethodHandler = ( req, res ) => {
|
|
152
|
+
if ( req.method.toLowerCase () === 'options' ) {
|
|
153
|
+
const allowHeaders = [ '*' ];
|
|
154
|
+
|
|
155
|
+
if ( res.methods.indexOf ( 'OPTIONS' ) === -1 ) res.methods.push ( 'OPTIONS' );
|
|
156
|
+
|
|
157
|
+
res.header ( 'Access-Control-Allow-Credentials', true );
|
|
158
|
+
res.header ( 'Access-Control-Allow-Headers', allowHeaders.join ( ', ' ) );
|
|
159
|
+
res.header ( 'Access-Control-Allow-Methods', res.methods.join ( ', ' ) );
|
|
160
|
+
res.header ( 'Access-Control-Allow-Origin', req.headers.origin );
|
|
161
|
+
|
|
162
|
+
return res.send ( 204 );
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return res.send ( new restifyErrors.MethodNotAllowedError ( { code: 405 }, `${ req.method } method is not available on this endpoint` ) );
|
|
166
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "just-another-http-api",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A framework built on top of restify aimed at removing the need for any network or server configuration. ",
|
|
5
|
+
"homepage": "https://github.com/OllieEdge/just-another-http-api#readme",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/OllieEdge/just-another-http-api.git"
|
|
9
|
+
},
|
|
10
|
+
"main": "api.js",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "node api.js",
|
|
13
|
+
"test": "mocha"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"restapi",
|
|
17
|
+
"restful",
|
|
18
|
+
"rest",
|
|
19
|
+
"restify",
|
|
20
|
+
"http",
|
|
21
|
+
"server",
|
|
22
|
+
"just another",
|
|
23
|
+
"another"
|
|
24
|
+
],
|
|
25
|
+
"author": "Oliver Edgington <oliver@edgington.com> (https://github.com/OllieEdge)",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"multer": "^1.4.2",
|
|
29
|
+
"recursive-readdir": "^2.2.2",
|
|
30
|
+
"restify": "github:restify/node-restify#c5361e9",
|
|
31
|
+
"restify-cors-middleware2": "^2.1.2",
|
|
32
|
+
"restify-errors": "^8.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"chai": "^4.3.6",
|
|
36
|
+
"chai-as-promised": "^7.1.1",
|
|
37
|
+
"mocha": "^10.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|