just-another-http-api 1.0.4 → 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 CHANGED
@@ -7,206 +7,456 @@
7
7
 
8
8
 
9
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
10
 
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.
11
+ ## Table of Contents
13
12
 
13
+ 1. [Introduction](#introduction)
14
+ 2. [Installation](#installation)
15
+ 3. [Terminology](#terminology)
16
+ 4. [Quickstart](#quickstart)
17
+ 5. [Configuration](#configuration)
18
+ 6. [Examples](#examples)
19
+ --
20
+ 1. [Upload](#uploads)
21
+ 2. [Caching](#caching)
22
+ 3. [Authentication](#authentication)
23
+
24
+ ## Introduction
25
+ This module provides a comprehensive but *extremely* simple solution for creating HTTP servers with support for features like caching, authentication, file uploads, and more. It leverages Fastify for high performance and includes integrations with Redis for caching and AWS S3 for file uploads. This lightweight HTTP API is used by businesses receiving over 100,000 requests every minute but can also been seen as a quick solution for those devs who want to get something up and running quickly! Unique for its simplicity and efficiency, it's designed to streamline the process of setting up an HTTP server.
26
+
27
+ ## Installation
28
+ To use this module, you need to have Node v18+ and npm installed. You can then include it in your project by cloning the repository or copying the module file.
29
+
30
+ ## Terminology
31
+ - **Endpoint** - A url that points to a particular part of a servers functionality e.g. `domain.com/endpoint`
32
+ - **Endpoint Handler** - The javascript file that handles that endpoint e.g. `./routes/endpoint/index.js`
33
+ - **Endpoint Handler Methods** - The function that handles the particular HTTP Method for that endpoint e.g. `exports.get` in `./routes/endpoint/index.js` would handle HTTP GET requests to `domain.com/endpoint`
34
+ - **Endpoint Handler Config** The config object defined in an endpoint handler by `exports.config`
35
+ - **Global Config** The config that is parse to **just-another-http-api** during initialization e.g. `const server = await justAnotherHttpApi ( globalConfig )`
14
36
 
15
37
  ## 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
38
 
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:
39
+ - Create new project in directory of your choice - `npm init`
40
+ - Install module `npm i just-another-http-api`
41
+ - Copy the following in to your `index.js`
42
+
43
+ ```javascript
44
+ const justAnotherHttpApi = require ( 'just-another-http-api' );
45
+
46
+ const globalConfig = {
47
+ name: 'My Server',
48
+ port: 4500
49
+ };
50
+
51
+ ( async () => {
52
+ const server = await justAnotherHttpApi( globalConfig );
53
+ console.log ( 'Server Ready' );
54
+ } ) ();
27
55
  ```
56
+
57
+ - Add a new endpoint by creating a the following folder structure: `/routes/myendpoint/index.js`
58
+ - Copy the following to `/routes/myendpoint/index.js`
59
+
60
+ ```javascript
61
+ const response = require ( 'just-another-http-api/utils/response' );
62
+
28
63
  exports.get = async req => {
29
- return {
30
- "hello": "world"
31
- };
32
- }
64
+ return response ( { json: { hello: 'world' } } );
65
+ };
33
66
  ```
34
- - Run server: `npm start`
35
- - Load http://localhost:4001/endpoint in your browser and you will see the JSON response.
36
67
 
68
+ - Run your server `node index.js`
69
+ - Open a browser and navigate to `http://localhost:4500/myendpoint` and see your response.
70
+
71
+ ## Configuration
72
+ The HTTP API module requires a configuration object to initialize. Here's a breakdown of the configuration options:
73
+
74
+ - `name`: Name of the server.
75
+ - `cache`: Caching configuration.
76
+ - `defaultExpiry`: Default cache expiry time in seconds.
77
+ - `enabled`: Enable or disable caching.
78
+ - `addCacheHeaders`: Add cache-related headers to the response.
79
+ - `redisClient`: Redis client instance.
80
+ - `redisPrefix`: Prefix for Redis cache keys.
81
+ - `auth`: Authentication configuration.
82
+ - `requiresAuth`: Global flag to require authentication.
83
+ - `type`: Type of authentication (currently only JWT).
84
+ - `jwtSecret`: Secret key for JWT.
85
+ - `jwtLoginHandle`: Function to handle login logic.
86
+ - `jwtExpiresIn`: JWT expiry time in seconds.
87
+ - `jwtEnabledRefreshTokens`: Enable or disable refresh tokens.
88
+ - `jwtStoreRefreshToken`: Function to store refresh tokens.
89
+ - `jwtRetrieveRefreshToken`: Function to retrieve refresh tokens.
90
+ - `jwtRefreshExpiresIn`: Expiry time for refresh tokens.
91
+ - `docRoot`: Directory containing route definitions.
92
+ - `port`: Port number for the server.
93
+ - `logs`: Enable or disable logging. Accepts a function or false.
94
+ - `uploads`: File upload configuration.
95
+ - `enabled`: Enable or disable file uploads.
96
+ - `storageType`: Type of storage ('s3', 'memory', or 'filesystem').
97
+ - `localUploadDirectory`: Directory for local file uploads.
98
+ - `s3Client`: AWS S3 client instance.
99
+ - `s3UploadDirectory`: S3 directory for file uploads.
100
+ - `s3UploadBucket`: S3 bucket for file uploads.
101
+ - `cors`: CORS configuration.
102
+ - `middleware`: Array of additional middleware functions.
103
+
104
+ ## Examples
105
+ Here's an example of how to set up the **global configuration**:
106
+
107
+ ```javascript
108
+ const getConfig = async () => {
109
+ // Initialize Redis and S3 clients, and define authentication functions
110
+ ...
111
+ return {
112
+ name: 'Server Name',
113
+ cache: {...}, // See Caching section for details
114
+ auth: {...}, // See Authentication section for details
115
+ docRoot: './routes', // This is where you store your endpoint handlers
116
+ port: 4500,
117
+ logs: false, // Accepts function or false
118
+ uploads: {...}, // See Uploads section for details
119
+ cors: {
120
+ allowedHeaders: [
121
+ 'accept',
122
+ 'accept-version',
123
+ 'content-type',
124
+ 'request-id',
125
+ 'origin',
126
+ ],
127
+ exposedHeaders: [
128
+ 'accept',
129
+ 'accept-version',
130
+ 'content-type',
131
+ 'request-id',
132
+ 'origin',
133
+ 'x-cache',
134
+ 'x-cache-age',
135
+ 'x-cache-expires',
136
+ ],
137
+ origin: '*',
138
+ methods: 'GET,PUT,POST,DELETE,OPTIONS',
139
+ optionsSuccessStatus: 204
140
+ },
141
+ middleware: [] // Currently not implemented
142
+ };
143
+ };
144
+ ```
37
145
 
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.
146
+ Here's an example of how to set up the **endpoint handler configuration**:
40
147
 
41
- | Node Release | Supported in Current Version | Notes |
42
- | :--: | :---: | :---: |
43
- | 18.x | **Yes** | Current stable |
44
- | <18 | **No** | No Support |
148
+ ```javascript
149
+ // ./routes/endpoint/index.js
150
+ exports.config = {
151
+ get: {
152
+ cache: true,
153
+ expires: 50, //seconds
154
+ requiresAuth: false
155
+ },
156
+ post: {
157
+ upload: {
158
+ enabled: true,
159
+ storageType: 'filesystem', // memory, filesystem, s3 (overrides the global config)
160
+ requestFileKey: 'data', // defaults to "files"
161
+ maxFileSize: 1000 * 1000 * 1000 * 1000 * 2, // 2GB
162
+ maxFiles: 1,
163
+ s3ACL: 'public-read',
164
+ subDirectory: 'test'
165
+ },
166
+ cache: false
167
+ }
168
+ };
45
169
 
46
- # Usage
47
- ## Initialise server
48
- Pretty simple to get the server running, use one of the following options:
170
+ // endpoint method handlers
171
+ exports.get = async ( req ) => { ... }
172
+ exports.post = async ( req ) => { ... }
173
+ // ...etc
49
174
  ```
50
- const API = require ( 'just-another-http-api' );
51
175
 
52
- // Option 1
53
- const server = await API();
176
+ ## Uploads
54
177
 
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 );
178
+ The upload functionality in the API allows for file uploads with various storage options including in-memory, filesystem, and Amazon S3. The configuration is flexible and can be set globally or overridden on a per-endpoint basis.
62
179
 
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
- ```
180
+ ### Configuration
74
181
 
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();
182
+ Uploads are configured through the `exports.config` object. Here's an example of how you can configure uploads for a particular endpoint handler:
183
+
184
+ ```javascript
185
+ exports.config = {
186
+ post: {
187
+ upload: {
188
+ enabled: true,
189
+ storageType: 's3', // Options: 'memory', 'filesystem', 's3'
190
+ requestFileKey: 'data', // Field name in the multipart form, defaults to 'files'
191
+ maxFileSize: 1000 * 1000 * 1000 * 1000 * 2, // Maximum file size, here set to 2GB
192
+ maxFiles: 5, // Maximum number of files
193
+ s3ACL: 'public-read', // S3 Access Control List setting
194
+ subDirectory: 'test' // Optional subdirectory for file storage in S3
195
+ },
196
+ cache: false
197
+ }
198
+ };
78
199
  ```
79
200
 
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.
201
+ ### Storage Options
202
+
203
+ - **Memory**: Stores the files in the server's memory. Suitable for small files and testing environments.
204
+ - **Filesystem**: Stores the files on the server's file system. Requires setting the `localUploadDirectory` in the global configuration.
205
+ - **S3**: Stores the files in an Amazon S3 bucket. Requires the S3 client configuration.
206
+
207
+ ### Handling Uploads
208
+
209
+ The upload handler middleware is automatically invoked for endpoints configured with upload functionality. It handles the file upload process based on the specified configuration, including the management of storage and any necessary cleanup in case of errors.
210
+
211
+ ### Error Handling
212
+
213
+ The upload middleware provides comprehensive error handling to cover various scenarios such as file size limits, unsupported file types, and storage issues. Users will receive clear error messages guiding them to resolve any issues that may arise during the file upload process.
214
+
215
+
216
+ ## Accessing Uploaded Files
217
+
218
+ After configuring the upload settings, you can access the uploaded files in your request handler. The uploaded file data will be available in the request (`req`) object. Depending on the storage type and the `maxFiles` setting, the structure of the uploaded file data may vary.
219
+
220
+ ### S3 Storage
221
+ When using S3 storage, the uploaded files are available in the `req.files` array. Each file in the array is an object containing metadata and the path of the uploaded file in the S3 bucket. For example:
222
+
223
+ ```json
224
+ [
225
+ {
226
+ "fieldname": "<input field name>",
227
+ "originalname": "<original file name>",
228
+ "encoding": "7bit",
229
+ "mimetype": "<mime type>",
230
+ "path": "<S3 bucket path>"
231
+ },
232
+ ...
233
+ ]
82
234
  ```
83
- // Config Object
84
235
 
236
+ If `maxFiles` is set to 1, the file information will be available as a single object in `req.file`.
237
+
238
+ ### Memory Storage
239
+ When using memory storage, the file's content is stored in memory. The file data can be accessed through `req.file` or `req.files` depending on `maxFiles`. An example structure is:
240
+
241
+ ```json
85
242
  {
86
- docRoot: 'customFolder'
243
+ "fieldname": "<input field name>",
244
+ "originalname": "<original file name>",
245
+ "encoding": "7bit",
246
+ "mimetype": "<mime type>",
247
+ "buffer": "<file data buffer>",
248
+ "size": <file size in bytes>
87
249
  }
88
250
  ```
89
251
 
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)
252
+ ### Filesystem Storage
253
+ For filesystem storage, the file is saved to the specified directory on the server. The file information, including the path to the saved file, can be accessed in a similar way:
254
+
255
+ ```json
256
+ {
257
+ "fieldname": "<input field name>",
258
+ "originalname": "<original file name>",
259
+ "encoding": "7bit",
260
+ "mimetype": "<mime type>",
261
+ "destination": "<file save directory>",
262
+ "filename": "<generated file name>",
263
+ "path": "<full file path>",
264
+ "size": <file size in bytes>
265
+ }
122
266
  ```
123
- ---
124
- ### Configuring your endpoint logic
125
- Each endpoint can export any of the follow http methods: `head, get, post, put, patch, del`
126
267
 
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
268
+ ## Caching
269
+ The API module provides a robust caching system to enhance performance and reduce load on the server. This system caches responses based on the request URL and query parameters.
128
270
 
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:
271
+ ### Configuration
272
+ Caching can be configured in the handlerConfig for each endpoint. Here is an example configuration:
273
+
274
+ ```javascript
275
+ exports.config = {
276
+ get: {
277
+ cache: true,
278
+ expires: 50, // seconds
279
+ },
280
+ post: {
281
+ cache: false
282
+ }
283
+ };
130
284
  ```
131
- exports.get = async req => {
132
285
 
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;
286
+ In this configuration:
135
287
 
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 }`);
288
+ - **GET** requests are cached for 50 seconds.
289
+ - **POST** requests are not cached.
138
290
 
139
- // Return the data in the response
140
- return user;
141
- }
291
+ ### How It Works
292
+ When a request is made, the cache middleware checks if a valid, non-expired cache entry exists.
293
+ If a valid cache exists, the response is served from the cache, bypassing the handler.
294
+ If no valid cache is found, the request proceeds to the handler, and the response is cached for future use.
295
+
296
+ ### Features
297
+ - **Default Expiry**: You can set a default cache expiry time in the global configuration.
298
+ - **Custom Expiry**: Each endpoint can have a custom cache expiry time.
299
+ - **Cache Headers**: The module can add cache-related headers (Age, Cache-Control, etc.) to responses.
300
+ - **Redis Integration**: The caching system is integrated with Redis for efficient, scalable storage.
301
+
302
+ ### Usage
303
+ The provided example below assumes your docRoot is `./routes`. The following file would be `./routes/example/index.js`
304
+
305
+ Below would provide you with the following endpoints:
306
+ - **GET** `http://localhost:4500/example` (cached)
307
+ - **POST** `http://localhost:4500/example` (not cached)
308
+
309
+ ```javascript
310
+ const response = require ( 'just-another-http-api/utils/response' );
311
+
312
+ exports.config = {
313
+ get: {
314
+ cache: true,
315
+ expires: 50, //seconds
316
+ },
317
+ post: {
318
+ cache: false
319
+ }
320
+ };
142
321
 
322
+ exports.get = async req => {
323
+ return response ( { html: '<p>hello world</p>' } );
324
+ };
143
325
  exports.post = async req => {
326
+ return req.body ;
327
+ };
328
+
329
+ ```
144
330
 
145
- // extract the JSON we sent in the request. Requires config.bodyParser to be true
146
- const { body } = req;
147
- const { userId } = req.params;
331
+ The caching system handles the caching and retrieval of responses automatically, based on the provided configuration. Responses that are cached do not execute any code in your endpoint method handler.
148
332
 
149
- // Update a user login goes here
150
- const user = {};
333
+ ### Cache Headers
334
+ The API module utilizes specific headers to convey caching information to clients. These headers are included in responses to indicate whether the data was served from the cache or generated afresh, as well as to provide information about the age and expiry of the cache data.
151
335
 
152
- return user;
336
+ Headers for Cache Miss (Data not served from cache)
337
+ When the response data is not served from the cache (Cache Miss), the following headers are added:
338
+
339
+ - **X-Cache**: Set to 'MISS' to indicate that the response was generated anew and not served from the cache.
340
+ - **X-Cache-Age**: Set to 0, as the response is fresh.
341
+ - **X-Cache-Expires**: Indicates the time (in seconds) until this response will be cached. This is determined by the expires configuration in the handlerConfig or the default cache expiry time.
342
+
343
+
344
+ When the response data is served from the cache (Cache Hit), the following headers are included:
345
+
346
+ - **X-Cache**: Set to 'HIT' to indicate that the response was served from the cache.
347
+ - **X-Cache-Age**: Shows the age of the cached data in seconds.
348
+ - **X-Cache-Expires**: The maximum age of the cache entry. When `X-Cache-Age` reaches this number is will no longer be cached.
349
+
350
+ These headers provide valuable insights into the caching status of the response, helping clients to understand the freshness and validity of the data they receive.
351
+
352
+ ## Authentication
353
+
354
+ ### Overview
355
+ The API module supports JWT (JSON Web Token) based authentication. This provides a secure way to handle user authentication and authorization throughout the API.
356
+
357
+ ### Configuration
358
+ Authentication is configurable through the `auth` object in the global configuration. Here are the key options and their descriptions:
359
+
360
+ - **requiresAuth**: A boolean value to enable or disable authentication globally. Default is `false`.
361
+ - **type**: Currently, the module supports only JWT authentication.
362
+ - **tokenEndpoint**: The endpoint you want to use for authenticating a new user. e.g.`/auth/login` (Default)
363
+ - **refreshTokenEndpoint**: The endpoint you want to use for refreshing tokens. e.g.`/auth/refresh` (Default)
364
+ - **jwtSecret**: A secret key used to sign the JWT tokens.
365
+ - **jwtLoginHandle**: A promise-based function to handle the login logic. It should return a user identifier (like a username) on successful login, or `false` on failure.
366
+ - **jwtExpiresIn**: Token expiry time in seconds. For example, `3600` for 1 hour.
367
+ - **jwtEnabledRefreshTokens**: Boolean value to enable or disable refresh tokens.
368
+ - **jwtStoreRefreshToken**: A promise-based function to store the refresh token. Typically, it involves storing the token in a database or a cache system.
369
+ - **jwtRetrieveRefreshToken**: A promise-based function to retrieve and validate the stored refresh token.
370
+ - **jwtRefreshExpiresIn**: Refresh token expiry time in seconds. For example, `604800` for 1 week.
371
+
372
+ ### Implementation
373
+ The authentication logic is integrated with the Fastify server instance. The `auth` plugin is responsible for setting up the necessary routes and middleware for handling JWT-based authentication.
374
+
375
+ ### Example
376
+
377
+ Coniguration for JWT auth:
378
+ ```javascript
379
+ const config = {
380
+ auth: {
381
+ requiresAuth: true,
382
+ tokenEndpoint: '/auth/login',
383
+ refreshTokenEndpoint: '/auth/refresh',
384
+ type: 'jwt', //only support for JWT currently
385
+ jwtSecret: 'secretkey', // can be any string
386
+ jwtLoginHandle: authenticateNewUser, // promise - see example below
387
+ jwtExpiresIn: 3600, // 1 hour
388
+ jwtEnabledRefreshTokens: true,
389
+ jwtStoreRefreshToken: storeRefreshToken, // promise - see example below
390
+ jwtRetrieveRefreshToken: retrieveRefreshToken, // promise - see example below
391
+ jwtRefreshExpiresIn: 604800, // 1 week
392
+ }
153
393
  }
394
+ ```
154
395
 
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' ) };
396
+ Example for authenticating a new user:
397
+ ```javascript
398
+ // Add this as the `jwtLoginHandle` in the config
399
+ const authenticateNewUser = async ( requestBody ) => {
400
+ const { username, password } = requestBody;
401
+
402
+ // Do your own authentication here, this is just an example
403
+ if ( username === 'admin' && password === 'admin' ) {
404
+
405
+ return 'username'; // A unique identifier that can be used to identify the user
406
+ }
407
+ else return false; // Login failed
408
+ };
158
409
  ```
159
- ---
160
- ## Example configuration
161
- When initialising the API you can parse it a config object. Below is a typical variant:
410
+
411
+ Example for storing a refresh token:
412
+ ```javascript
413
+ // Add this as the `jwtStoreRefreshToken` in the config
414
+ const storeRefreshToken = async ( username, refreshToken ) => {
415
+ await redis.set ( `refresh-token:${ username }`, refreshToken, 60 * 60 * 24 * 30 ); // Set expiry here
416
+
417
+ return;
418
+ };
162
419
  ```
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
- }
420
+
421
+ Example for retrieving a refresh token:
422
+ ```javascript
423
+ // Add this as the `jwtRetrieveRefreshToken` in the config
424
+ const retrieveRefreshToken = async ( username, refreshToken ) => {
425
+ const storedRefreshToken = await redis.get ( `refresh-token:${ username }` );
426
+
427
+ return storedRefreshToken === refreshToken;
193
428
  };
194
429
  ```
195
430
 
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 | -
431
+ ### Usage
432
+ To use JWT authentication, the `requiresAuth` flag must be set to `true` in the global configuration or at the endpoint level. The API will then require a valid JWT token for accessing protected routes.
433
+
434
+ #### Generating Tokens
435
+ Upon successful login (as determined by the `jwtLoginHandle` function), the API generates a JWT token based on the provided `jwtSecret` and `jwtExpiresIn` configuration.
436
+
437
+ #### Refresh Tokens
438
+ If `jwtEnabledRefreshTokens` is set to `true`, the API also generates a refresh token, allowing users to obtain a new access token without re-authenticating. This token is managed by the `jwtStoreRefreshToken` and `jwtRetrieveRefreshToken` functions.
439
+
440
+ #### Token Validation
441
+ For each request to a protected route, the API validates the provided JWT token. If the token is invalid or expired, the request is denied with an appropriate error message.
442
+
443
+ ### Security
444
+ - Ensure the `jwtSecret` is kept secure and confidential.
445
+ - Regularly rotate the `jwtSecret` to maintain security.
446
+ - Implement robust login logic in the `jwtLoginHandle` function to prevent unauthorized access.
447
+
448
+ By integrating JWT authentication, the API ensures secure and efficient user authentication and authorization.
449
+
450
+ ## Contributing
451
+ Contributions to improve the module or add new features are welcome. Please follow the standard GitHub pull request process.
204
452
 
453
+ ## License
454
+ Specify the license under which this module is released.
205
455
 
206
456
  ## LICENSE
207
457
  MIT License
208
458
 
209
- Copyright (c) 2022 Oliver Edgington
459
+ Copyright (c) 2024 Oliver Edgington
210
460
 
211
461
  Permission is hereby granted, free of charge, to any person obtaining a copy
212
462
  of this software and associated documentation files (the "Software"), to deal