express-version-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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Braian Quintian
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,146 @@
1
+ # Express version api
2
+
3
+ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/Braian-Quintian/express-version-routes?tab=MIT-1-ov-file#readme)
4
+
5
+ Express version api is a simple library that allows you to create versioned routes in your Express.js application. It provides a middleware function that allows you to define different route handlers based on the requested API version. This can be useful when you need to maintain multiple versions of an API and serve different responses based on the version number specified by the client.
6
+
7
+ **🛑Note:** This library is intended for personal use and experimentation, and is **_not recommended_** for production environments or widespread adoption.
8
+
9
+ # 🚧 Under Development 🚧
10
+
11
+ **This library is actively being developed. Improvements and new features are added regularly. As such, there's no stable version and changes are frequent. Please consider this when using the library.**
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install express-version-api
17
+ ```
18
+
19
+ ## Disclaimer
20
+
21
+ **⚠️ Disclaimer: This library is intended for personal use and experimentation. While it is released under the MIT license, allowing anyone to use and modify it, please note the following considerations:**
22
+
23
+ - **Not Recommended for Production:** This library is designed for educational and personal use. It is not optimized for production environments or widespread adoption. Use in production settings is discouraged.
24
+
25
+ - **Limited Support and Maintenance:** As this is a personal project, there may be limited ongoing support or maintenance. Contributors are welcome, but please manage expectations accordingly.
26
+
27
+ - **Not a Replacement for Established Libraries:** This library is not intended to replace established or widely used libraries for API versioning in production applications. Consider using dedicated solutions with robust community support for production environments.
28
+
29
+ ---
30
+
31
+ ## Usage
32
+
33
+ > You can use the `versionApi` middleware to create versioned routes in your Express.js application. The `versionApi` middleware accepts an object with the following properties:
34
+
35
+ - `version` (string): The version of the API.
36
+ - `function` (function): The function that will be called when the route is accessed.
37
+
38
+ > Follows semver versioning format. Supports '^, ~' symbols for matching version numbers.
39
+
40
+ ### Example basic:
41
+
42
+ ```javascript
43
+ const express = require("express");
44
+ const versionApi = require("express-version-api");
45
+ const app = express();
46
+
47
+ // Define your route with versioned handlers
48
+ app.get(
49
+ "/api",
50
+ versionApi({
51
+ "1.0.0": functionV1, // Route handler for API version 1.0.0
52
+ "2.0.0": functionV2, // Route handler for API version 2.0.0
53
+ })
54
+ );
55
+
56
+ // Handler for version 1.0.0
57
+ function functionV1(req, res) {
58
+ res.send("This is version 1.0.0"); // Sends a response for API version 1.0.0
59
+ }
60
+
61
+ // Handler for version 2.0.0
62
+ function functionV2(req, res) {
63
+ res.send("This is version 2.0.0"); // Sends a response for API version 2.0.0
64
+ }
65
+
66
+ app.listen(3000, () => {
67
+ console.log("Server is running on port 3000");
68
+ });
69
+ ```
70
+
71
+ ### Example using '^, ~' semver symbols:
72
+
73
+ > The `^` symbol is used to match the specified version and any minor or patch updates. The `~` symbol is used to match the specified version and any patch updates.
74
+
75
+ **Caret Operator** **_(^)_**: Matches the specified version and any minor or patch updates. For example, `^1.0.0` will match `1.0.0`, `1.1.0`, `1.1.1`, etc., but not `2.0.0`.
76
+
77
+ **Tilde Operator** **_(~)_**: Matches the specified version and any patch updates. For example, ~1.0.0 will match `1.0.0`, `1.0.1`, `1.0.2`, etc., but not `1.1.0`.
78
+
79
+ ```javascript
80
+ const express = require("express");
81
+ const versionApi = require("express-version-api");
82
+ const app = express();
83
+
84
+ // Define your route with versioned handlers
85
+ app.get(
86
+ "/api",
87
+ versionApi({
88
+ "^1.0.0": functionV1, // Route handler for API version 1.X.X Caret Operator
89
+ "~2.0.0": functionV2, // Route handler for API version 2.0.X Tilde Operator
90
+ })
91
+ );
92
+
93
+ // Handler for version 1.0.0
94
+ function functionV1(req, res) {
95
+ res.send("This is version 1.0.0"); // Sends a response for API version 1.X.X
96
+ }
97
+
98
+ // Handler for version 2.0.0
99
+ function functionV2(req, res) {
100
+ res.send("This is version 2.0.0"); // Sends a response for API version 2.0.X
101
+ }
102
+
103
+ app.listen(3000, () => {
104
+ console.log("Server is running on port 3000");
105
+ });
106
+ ```
107
+
108
+ ### Versioning via Headers
109
+
110
+ To specify the desired API version, clients should include the `Accept-Version` header in their requests with the version number they want to access. Here's an example of how to set the Accept-Version header using curl:
111
+
112
+ ```bash
113
+ curl -H "Accept-Version: 1.0.0" http://localhost:3000/api
114
+ ```
115
+
116
+ In this example, the server will invoke functionV1 when the request specifies Accept-Version: 1.0.0 and functionV2 when Accept-Version: 2.0.0 is specified.
117
+
118
+ Make sure to handle the Accept-Version header appropriately in your middleware or route handlers to serve the correct API version based on client preferences.
119
+
120
+ ## Running Tests
121
+
122
+ This library uses Jest for testing. To run the tests, you can use the following command:
123
+
124
+ ```bash
125
+ npm run test
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Roadmap
131
+
132
+ - Added support for semver features🌟
133
+ - Enhanced support for APIs starting with v1🌟
134
+ - Add more integrations and examples🌟
135
+
136
+ ### Development Status
137
+
138
+ This library is currently under active development. We are continuously working on improving and adding new features to enhance its functionality.
139
+
140
+ ## Contributing
141
+
142
+ We welcome contributions! If you're interested in improving this library or adding new features, we'd love to receive your contributions.
143
+
144
+ ## Feedback
145
+
146
+ If you have any feedback, please reach out to us at bquintian.developer@gmail.com
@@ -0,0 +1,2 @@
1
+ import versionApi from "./lib";
2
+ export default versionApi;
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const lib_1 = __importDefault(require("./lib"));
7
+ module.exports = lib_1.default;
8
+ exports.default = lib_1.default;
package/dist/lib.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Interface for version handlers.
3
+ * Each key is a version string and the value is a function to handle requests for that version.
4
+ */
5
+ interface VersionHandlers {
6
+ [key: string]: (req: any, res: any, next: any) => void;
7
+ }
8
+ /**
9
+ * Main middleware that handles version-based routing logic.
10
+ * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
11
+ * @param {((req: any, res: any, next: any) => void) | undefined} defaultHandler - The default handler function.
12
+ * @returns {((req: any, res: any, next: any) => void)} - The middleware function.
13
+ */
14
+ declare const versioningMiddleware: (versionHandlers: VersionHandlers, defaultHandler?: (req: any, res: any, next: any) => void) => ((req: any, res: any, next: any) => void);
15
+ export default versioningMiddleware;
package/dist/lib.js ADDED
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Extracts the version from the request object.
5
+ * @param {any} req - The request object.
6
+ * @returns {string | boolean} - The version string if it exists, otherwise false.
7
+ */
8
+ const extractVersionFromRequest = (req) => {
9
+ return req
10
+ ? req.version
11
+ ? String(req.version)
12
+ : req.headers["accept-version"]
13
+ : false;
14
+ };
15
+ /**
16
+ * Executes the corresponding version handler if there is a version match.
17
+ * @param {string} handlerKey - The key of the handler to execute.
18
+ * @param {string} clientVersion - The version from the client's request.
19
+ * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
20
+ * @param {any} req - The request object.
21
+ * @param {any} res - The response object.
22
+ * @param {any} next - The next middleware function.
23
+ * @returns {boolean} - True if a matching version handler was executed, otherwise false.
24
+ */
25
+ const executeVersionHandler = (handlerKey, clientVersion, versionHandlers, req, res, next) => {
26
+ let tempKey;
27
+ let versionArray = clientVersion.split(".");
28
+ let tempVersion;
29
+ if (handlerKey[0] === "~") {
30
+ // Handling versions using '~'
31
+ tempKey = handlerKey.substring(1).split(".").slice(0, 2).join(".");
32
+ versionArray[1] = versionArray[1] || "0";
33
+ tempVersion = versionArray.slice(0, 2).join(".");
34
+ }
35
+ else if (handlerKey[0] === "^") {
36
+ // Handling versions using '^'
37
+ tempKey = handlerKey.substring(1).split(".").slice(0, 1).join(".");
38
+ tempVersion = versionArray.slice(0, 1).join(".");
39
+ }
40
+ else {
41
+ // Standard version handling
42
+ tempKey = handlerKey;
43
+ versionArray[1] = versionArray[1] || "0";
44
+ versionArray[2] = versionArray[2] || "0";
45
+ tempVersion = versionArray.join(".");
46
+ }
47
+ if (tempKey === tempVersion && versionHandlers[handlerKey]) {
48
+ // Call the corresponding version handler if there is a match
49
+ versionHandlers[handlerKey].call(this, req, res, next);
50
+ return true;
51
+ }
52
+ return false;
53
+ };
54
+ /**
55
+ * Executes the default handler if no matching version is found.
56
+ * @param {((req: any, res: any, next: any) => void) | undefined} defaultHandler - The default handler function.
57
+ * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
58
+ * @param {string[]} keys - The keys of all version handlers.
59
+ * @param {any} req - The request object.
60
+ * @param {any} res - The response object.
61
+ * @param {any} next - The next middleware function.
62
+ */
63
+ const executeDefaultHandler = (defaultHandler, versionHandlers, keys, req, res, next) => {
64
+ // Check if there is a defaultHandler defined and execute it if it exists
65
+ if (defaultHandler) {
66
+ defaultHandler(req, res, next);
67
+ }
68
+ else {
69
+ // If there is no defaultHandler, determine the most recent version
70
+ const latestVersionKey = findLatestVersion(keys);
71
+ // If the latest version is found and there is a handler for that version, execute it
72
+ if (latestVersionKey && versionHandlers[latestVersionKey]) {
73
+ versionHandlers[latestVersionKey](req, res, next);
74
+ }
75
+ else {
76
+ // If no valid version is found to handle the request, respond with a 422 error
77
+ res.status(422).send("Unprocessable Entity: No valid handler found for this request.");
78
+ }
79
+ }
80
+ };
81
+ /**
82
+ * Main middleware that handles version-based routing logic.
83
+ * @param {VersionHandlers} versionHandlers - The object containing all version handlers.
84
+ * @param {((req: any, res: any, next: any) => void) | undefined} defaultHandler - The default handler function.
85
+ * @returns {((req: any, res: any, next: any) => void)} - The middleware function.
86
+ */
87
+ const versioningMiddleware = (versionHandlers, defaultHandler) => {
88
+ if (!versionHandlers ||
89
+ typeof versionHandlers !== "object" ||
90
+ Array.isArray(versionHandlers)) {
91
+ throw new Error("Invalid argument: 'versionHandlers' must be a non-array object.");
92
+ }
93
+ else {
94
+ return (req, res, next) => {
95
+ const clientVersion = extractVersionFromRequest(req);
96
+ const keys = Object.keys(versionHandlers);
97
+ if (!clientVersion) {
98
+ // Execute the default handler if no version is provided
99
+ return res.status(422).send("Unprocessable Entity: No version provided.");
100
+ }
101
+ for (let i = 0; i < keys.length; i++) {
102
+ const handlerKey = keys[i];
103
+ if (executeVersionHandler(handlerKey, clientVersion, versionHandlers, req, res, next)) {
104
+ return;
105
+ }
106
+ }
107
+ // If no matching version is found, execute the default handler
108
+ executeDefaultHandler(defaultHandler, versionHandlers, keys, req, res, next);
109
+ };
110
+ }
111
+ };
112
+ /**
113
+ * Finds the latest version among an array of versions.
114
+ * @param {string[]} versions - The array of version strings.
115
+ * @returns {string | null} - The latest version string, or null if the array is empty.
116
+ */
117
+ const findLatestVersion = (versions) => {
118
+ // Find the latest version among an array of versions
119
+ if (!versions || versions.length === 0) {
120
+ return null;
121
+ }
122
+ const processedVersions = versions.map((version) => {
123
+ // Process versions by removing special characters and padding with zeros
124
+ const versionArr = version.replace(/[\^~]/g, "").split(".");
125
+ for (let i = 0; i < 2; i++) {
126
+ versionArr[i] = versionArr[i] || "0";
127
+ }
128
+ return versionArr;
129
+ });
130
+ // Sort processed versions to find the latest one
131
+ processedVersions.sort((v1Arr, v2Arr) => {
132
+ for (let i = 0; i < 2; i++) {
133
+ if (Number(v1Arr[i]) > Number(v2Arr[i])) {
134
+ return 1;
135
+ }
136
+ else if (Number(v1Arr[i]) < Number(v2Arr[i])) {
137
+ return -1;
138
+ }
139
+ }
140
+ return 0;
141
+ });
142
+ return processedVersions[processedVersions.length - 1].join(".");
143
+ };
144
+ // Exporta la función para CommonJS
145
+ module.exports = versioningMiddleware;
146
+ // Exporta la función como default para ES6 modules
147
+ exports.default = versioningMiddleware;
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "express-version-api",
3
+ "description": "Middleware for versioning routes/APIs in Express.js using ECMAScript 6. Note: This library is intended for personal use and experimentation, and is not recommended for production environments or widespread adoption.",
4
+ "version": "1.0.1",
5
+ "author": "Braian Quintian",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/Braian-Quintian/express-version-routes?tab=readme-ov-file",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Braian-Quintian/express-version-api"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/Braian-Quintian/express-version-routes/issues"
14
+ },
15
+ "main": "dist/index.js",
16
+ "engines": {
17
+ "node": ">=20.0.0"
18
+ },
19
+ "scripts": {
20
+ "build": "npx tsc",
21
+ "test": "jest --verbose"
22
+ },
23
+ "keywords": [
24
+ "routes",
25
+ "api",
26
+ "version",
27
+ "versioning"
28
+ ],
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "devDependencies": {
33
+ "@types/express": "^4.17.21",
34
+ "@types/jest": "^29.5.12",
35
+ "@types/supertest": "^6.0.2",
36
+ "express": "^4.19.2",
37
+ "jest": "^29.7.0",
38
+ "supertest": "^7.0.0",
39
+ "ts-jest": "^29.1.2"
40
+ }
41
+ }