codeweaver 1.0.9

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 Ehsan-Afzali-2024
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,203 @@
1
+ # A lightweight framework built on top of Express and TypeScript
2
+
3
+ ## Overview
4
+
5
+ **Codeweaver** is a lightweight framework built on top of `Express` and `TypeScript`, integrating `Swagger` for API documentation. The application utilizes `async handlers` for improved error management and follows a modular structure for routers, enabling easy expansion and organization of the application.
6
+
7
+ ## Features
8
+
9
+ - **Modular Router Structure**: Each router is automatically imported and mounted, providing clean separation of endpoints and logic.
10
+ - **Express Framework**: A lightweight web application framework for building web applications in Node.js.
11
+ - **TypeScript**: Provides strong typing for better development experience and less runtime errors.
12
+ - **Swagger Integration**: Automatically generates interactive API documentation, making it easy for developers and consumers to understand the available endpoints.
13
+ - **Async Handlers**: Supports async/await syntax for writing cleaner and more maintainable asynchronous code without deeply nested callbacks.
14
+
15
+ ## Technologies Used
16
+
17
+ - **Node.js**
18
+ - **Express**
19
+ - **TypeScript**
20
+ - **Zod** (for input validation)
21
+ - **ts-zod-decorators** (for validation using Zod with decorators)
22
+ - **utils-decorators** (for middleware utilities like throttling and error handling)
23
+ - **Swagger** (for API documentation)
24
+
25
+ ## Installation
26
+
27
+ To get started with the project, follow these steps:
28
+
29
+ 1. **Clone the repository**:
30
+
31
+ ```bash
32
+ git clone https://github.com/@js-code-crafter/codeweaver.git
33
+ cd codeweaver
34
+ ```
35
+
36
+ 2. **Install dependencies**:
37
+
38
+ ```bash
39
+ npm install
40
+ ```
41
+
42
+ 3. **Run the application**:
43
+
44
+ ```bash
45
+ npm start
46
+ ```
47
+
48
+ 4. **Visit the Swagger UI**: Open your browser and go to `http://localhost:3000/api-docs` to view the automatically generated API documentation.
49
+
50
+ 5. **Build**: Compile the TypeScript files for the production environment:
51
+
52
+ ```bash
53
+ npm run build
54
+ ```
55
+
56
+ ## Sample Project Structure
57
+
58
+ **/src**
59
+ ├── **/routers** `Directory containing all router files`
60
+ │ ├── **/user** `Routers for user-related endpoints`
61
+ │ │ ├── user_router1.ts `/user/user_router1`
62
+ │ │ ├── user_router2.ts `/user/user_router2`
63
+ │ │ ├── user.controller.ts
64
+ │ │ ├── user.service.ts
65
+ │ │ └── user.dto.ts
66
+ │ ├── **/product** `Routers for product-related endpoints`
67
+ │ │ ├── index.ts `/product`
68
+ │ │ ├── product_test.spec.ts
69
+ │ │ ├── product.controller.ts
70
+ │ │ ├── product.service.ts
71
+ │ │ ├── **/dto**
72
+ │ │ │ └── product.dto.ts
73
+ │ ├── **/order** `Routers for order-related endpoints`
74
+ │ │ ├── index.ts `/order`
75
+ │ │ ├── order_test.spec.ts
76
+ │ │ ├── order.controller.ts
77
+ │ │ └── order.service.ts
78
+ │ └── index.ts `Home page`
79
+ ├── app.ts `Main application file`
80
+ ├── config.ts `Application configuration file`
81
+ └── ... `Other files (middleware, models, etc.)`
82
+
83
+ ### Router Directory
84
+
85
+ Each router file in the `/routers` directory is organized to handle related endpoints. The `app.ts` file automatically imports all routers and mounts them to the main Express application, making it simple to add new routes without modifying central files.
86
+
87
+ Files that end with `.controller`, `.service`, `.spec`, `.dto`, `.middleware`, `.error`, or `.decorator`, as well as those that start with `_` or `@`, are excluded from the router list and can be utilized for various other purposes within the application.
88
+
89
+ Example of a basic router:
90
+
91
+ ```typescript
92
+ import { Router, Request, Response } from "express";
93
+ import asyncHandler from "express-async-handler";
94
+
95
+ const router = Router();
96
+
97
+ /**
98
+ * @swagger
99
+ * /:
100
+ * get:
101
+ * summary: Get the home page
102
+ * description: Returns the home page.
103
+ * responses:
104
+ * 200:
105
+ * description: home page
106
+ */
107
+ router.get(
108
+ "/",
109
+ asyncHandler(async (req: Request, res: Response) => {
110
+ res.send("Home");
111
+ })
112
+ );
113
+
114
+ export = router;
115
+ ```
116
+
117
+ ### Controllers
118
+
119
+ **Controllers** in this Express TypeScript framework act as the intermediary between the incoming HTTP requests and the application logic. Each controller is responsible for handling specific routes and defining the behavior associated with those routes. This organization promotes a clean architecture by separating business logic, validation, and routing concerns.
120
+
121
+ Controllers can be organized within the router folders, allowing them to stay closely related to their respective routes. However, they are not limited to this structure and can be placed anywhere within the `src` folder as needed, providing flexibility in organizing the codebase.
122
+
123
+ Controllers leverage decorators from the `ts-zod-decorators` package to implement input validation and error handling gracefully. With validators like `@Validate`, controllers can ensure that incoming data adheres to defined schemas before any processing occurs, preventing invalid data from reaching the service layer. This capability enhances data integrity and application reliability.
124
+
125
+ For example, in the provided `UserController`, the `createUser` method demonstrates how to apply input validation and error handling through decorators. It employs `@rateLimit` to restrict the number of allowed requests within a specified timeframe, effectively guarding against too many rapid submissions. When an error arises, the `@onError` decorator provides a systematic way to handle exceptions, allowing for logging or other error management processes to be performed centrally.
126
+
127
+ Here’s a brief breakdown of key components used in the `UserController`:
128
+
129
+ - **Validation**: The `CreateUserDto` is validated against incoming data using the `@ZodInput` decorator, ensuring that only well-formed data is passed to the business logic, which is crucial for maintaining application stability.
130
+
131
+ ```typescript
132
+ import { z } from "zod";
133
+
134
+ export const createUserDto = z.object({
135
+ username: z.string().min(3),
136
+ email: z.string().email(),
137
+ password: z.string().min(6),
138
+ });
139
+
140
+ export type CreateUser = z.infer<typeof createUserDto>;
141
+ ```
142
+
143
+ - **Throttling and Rate Limiting**: The `@rateLimit` decorator is applied to safeguard the application's endpoints from abuse by limiting how frequently a particular method can be invoked.
144
+
145
+ - **Error Handling**: The `@onError` decorator captures any exceptions that occur during the execution of the createUser method, allowing for centralized error management, which can greatly simplify debugging and improve maintainability.
146
+
147
+ By using a well-organized controller structure, this project makes it easier to add, modify, and manage endpoints as the application grows. Developers can focus on implementing business logic while the controllers handle the intricacies of request parsing, validation, and response formatting. Additionally, this separation of concerns improves unit testing, as controllers can be tested independently from the rest of the application logic, ensuring robust and reliable API behavior.
148
+
149
+ Here is a quick reference to the UserController in practice:
150
+
151
+ ```typescript
152
+ import { Validate, ZodInput } from "ts-zod-decorators";
153
+ import { createUserDto, CreateUser } from "./dto/user.dto";
154
+ import { onError, rateLimit } from "utils-decorators";
155
+
156
+ function exceedHandler() {
157
+ throw new Error("Too much call in allowed window");
158
+ }
159
+
160
+ function errorHandler(e: Error): void {
161
+ console.error(e);
162
+ }
163
+
164
+ export default class UserController {
165
+ //constructor(private readonly userService: UserService) { }
166
+
167
+ // Throttle the createUser method to 1 request per 200 milliseconds
168
+ @rateLimit({
169
+ timeSpanMs: 60000,
170
+ allowedCalls: 300,
171
+ exceedHandler,
172
+ })
173
+ @onError({
174
+ func: errorHandler,
175
+ })
176
+ @Validate
177
+ public async createUser(
178
+ @ZodInput(CreateUserDto) data: CreateUser
179
+ ): Promise<string> {
180
+ // Here you can include logic to save user to database
181
+ console.log("Creating user:", data);
182
+ return "User created successfully";
183
+ }
184
+ }
185
+ ```
186
+
187
+ This structure not only supports effective code organization but also ensures that each part of the application is working towards the same goal: a scalable, maintainable, and robust API.
188
+
189
+ ### Async Handlers
190
+
191
+ To maintain cleaner code and improve error handling, the app suggests the `express-async-handler` package that automatically catches exceptions inside of async express routes and passing them to the express error handlers. This allows you to focus on writing the business logic without worrying about try/catch blocks.
192
+
193
+ ### API Documentation
194
+
195
+ Once the application is running, visit the Swagger UI at http://localhost:3000/api-docs. This automatically generated documentation will provide you with all available routes along with details on request parameters, response structures, and possible error codes.
196
+
197
+ ### Decorators
198
+
199
+ To prevent abuse of your API, you can utilize throttling, and validation decorators from the `utils-decorators` and `ts-zod-decorators` packages respectively. This packages provides decorators that can be applied directly to your service and controller classes.
200
+
201
+ ### Contributing
202
+
203
+ Contributions are welcome! If you have suggestions for improvements or new features, feel free to create an issue or submit a pull request.
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "codeweaver",
3
+ "version": "1.0.9",
4
+ "main": "src/app.ts",
5
+ "scripts": {
6
+ "start": "nodemon src/app.ts",
7
+ "build": "tsc",
8
+ "serve": "node dist/app.js"
9
+ },
10
+ "keywords": [
11
+ "lightweight framework",
12
+ "framework",
13
+ "boilerplate",
14
+ "bootstrap",
15
+ "lightweight",
16
+ "nested routers"
17
+ ],
18
+ "author": "Ehsan Afzali",
19
+ "license": "MIT",
20
+ "description": "A lightweight framework built on top of Express and TypeScript",
21
+ "dependencies": {
22
+ "express": "^4.21.1",
23
+ "express-async-handler": "^1.2.0",
24
+ "ts-zod-decorators": "^1.7.1",
25
+ "utils-decorators": "^2.0.9",
26
+ "zod": "^3.23.8"
27
+ },
28
+ "devDependencies": {
29
+ "@types/express": "^5.0.0",
30
+ "@types/node": "^22.10.7",
31
+ "nodemon": "^3.1.7",
32
+ "swagger-jsdoc": "^6.2.8",
33
+ "swagger-ui-express": "^5.0.1",
34
+ "ts-node": "^10.9.2",
35
+ "typescript": "^5.6.3"
36
+ }
37
+ }
package/src/app.ts ADDED
@@ -0,0 +1,95 @@
1
+ import express from "express";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import config from "./config";
5
+
6
+ // Function to load routers recursively
7
+ function loadRouters(routerPath: string, basePath: string = "") {
8
+ fs.readdirSync(routerPath).forEach((file) => {
9
+ // Check if the filename should be excluded based on certain criteria
10
+ if (
11
+ // Exclude files that start with an underscore (_)
12
+ file.startsWith("_") ||
13
+ // Exclude files that start with an at symbol (@)
14
+ file.startsWith("@") ||
15
+ // Exclude JavaScript controller files
16
+ file.endsWith(".controller.js") ||
17
+ // Exclude TypeScript controller files
18
+ file.endsWith(".controller.ts") ||
19
+ // Exclude JavaScript service files
20
+ file.endsWith(".service.js") ||
21
+ // Exclude TypeScript service files
22
+ file.endsWith(".service.ts") ||
23
+ // Exclude JavaScript middleware files
24
+ file.endsWith(".middleware.js") ||
25
+ // Exclude TypeScript middleware files
26
+ file.endsWith(".middleware.ts") ||
27
+ // Exclude JavaScript error middleware files
28
+ file.endsWith(".error.js") ||
29
+ // Exclude TypeScript error middleware files
30
+ file.endsWith(".error.ts") ||
31
+ // Exclude JavaScript service files
32
+ file.endsWith(".decorator.js") ||
33
+ // Exclude TypeScript service files
34
+ file.endsWith(".decorator.ts") ||
35
+ // Exclude JavaScript DTO files
36
+ file.endsWith(".dto.js") ||
37
+ // Exclude TypeScript DTO files
38
+ file.endsWith(".dto.ts") ||
39
+ // Exclude JavaScript test specification files
40
+ file.endsWith(".spec.js") ||
41
+ // Exclude TypeScript test specification files
42
+ file.endsWith(".spec.ts")
43
+ )
44
+ // If any of the above conditions are true, exit the function early
45
+ return;
46
+
47
+ const fullPath = path.join(routerPath, file);
48
+
49
+ // Construct a route path by combining the base path with the filename
50
+ const routePath = path
51
+ // Join the base path and the filename, removing the file extension (.js or .ts)
52
+ .join(basePath, file.replace(/(\.js|\.ts)/g, ""))
53
+ // Replace all backslashes with forward slashes for consistent file path formatting
54
+ .replaceAll("\\", "/")
55
+ // Remove the trailing '/index' if it exists, to clean up the route
56
+ .replace(/\/?index$/g, "");
57
+
58
+ if (fs.lstatSync(fullPath).isDirectory()) {
59
+ // If the file is a directory, call the function again
60
+ loadRouters(fullPath, routePath);
61
+ } else if (file.endsWith(".ts") || file.endsWith(".js")) {
62
+ // Import the router module and mount it to the app
63
+ const router = require(fullPath);
64
+ app.use("/" + routePath, router);
65
+ console.log(`Mounted ${file} at ${"/" + routePath}`);
66
+ }
67
+ });
68
+ }
69
+
70
+ const app = express();
71
+ app.use(express.json());
72
+ app.use(express.urlencoded({ extended: true }));
73
+
74
+ //app.use(cors());
75
+
76
+ // Automatically import all routers from the /src/routers directory
77
+ const routersPath = path.join(__dirname, "/routers");
78
+ loadRouters(routersPath);
79
+
80
+ // Swagger setup
81
+ if (config.devMode) {
82
+ const swaggerJsDoc = require("swagger-jsdoc");
83
+ const swaggerUi = require("swagger-ui-express");
84
+ const swaggerDocs = swaggerJsDoc(config.swaggerOptions);
85
+ app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocs));
86
+ }
87
+
88
+ // Start the server
89
+ app.listen(config.port, () => {
90
+ console.log(`Server is running on http://localhost:${config.port}`);
91
+ if (config.devMode)
92
+ console.log(
93
+ `Swagger UI is available at http://localhost:${config.port}/api-docs`
94
+ );
95
+ });
package/src/config.ts ADDED
@@ -0,0 +1,65 @@
1
+ interface Server {
2
+ url: string;
3
+ }
4
+
5
+ interface Info {
6
+ title: string;
7
+ version: string;
8
+ description: string;
9
+ }
10
+
11
+ interface SwaggerDefinition {
12
+ openApi: string;
13
+ info: Info;
14
+ servers: Server[];
15
+ }
16
+
17
+ interface SwaggerOptions {
18
+ swaggerDefinition: SwaggerDefinition;
19
+ apis: string[];
20
+ }
21
+
22
+ interface Config {
23
+ devMode: boolean;
24
+ port: string;
25
+ swaggerOptions: SwaggerOptions;
26
+ }
27
+ const port = process.env.PORT || "3000";
28
+ const config: Config = {
29
+ devMode: process.env.NODE_ENV !== "production",
30
+ port,
31
+ swaggerOptions: {
32
+ swaggerDefinition: {
33
+ openApi: "3.0.0",
34
+ info: {
35
+ title: "Express Items API",
36
+ version: "1.0.0",
37
+ description: "A simple CRUD API with Swagger documentation",
38
+ },
39
+ servers: [
40
+ {
41
+ url: "http://localhost:" + port,
42
+ },
43
+ ],
44
+ },
45
+ apis: [
46
+ "./src/routers/index.ts",
47
+ "./src/routers/**/*.ts",
48
+ "./src/routers/index.js",
49
+ "./src/routers/**/*.js",
50
+ ], // Path to the API docs
51
+ },
52
+ };
53
+
54
+ // Other configurations:
55
+ //
56
+ // config.jwt_key = config.devMode ? "" : "";
57
+ // config.jwt_expiration = config.devMode ? 360000 : 360000;
58
+ // config.dbConnectionString = config.devMode ? `mongoDb url` : `mongoDb url`;
59
+ // config.mongoDebug = config.devMode;
60
+ // config.port = config.devMode ? 3000 : 3000;
61
+ // config.host = config.devMode ? "localhost" : "localhost";
62
+ // config.env = config.devMode ? "development" : "production";
63
+ // config.mongoUrl = config.devMode ? "mongodb://localhost:27017/test" : "mongodb://localhost:27017/test";
64
+
65
+ export default config;
@@ -0,0 +1,23 @@
1
+ import { Router, Request, Response } from "express";
2
+ import asyncHandler from "express-async-handler";
3
+
4
+ const router = Router();
5
+
6
+ /**
7
+ * @swagger
8
+ * /:
9
+ * get:
10
+ * summary: Get the home page
11
+ * description: Returns the home page.
12
+ * responses:
13
+ * 200:
14
+ * description: home page
15
+ */
16
+ router.get(
17
+ "/",
18
+ asyncHandler(async (req: Request, res: Response) => {
19
+ res.send("Home");
20
+ })
21
+ );
22
+
23
+ export = router;
@@ -0,0 +1,40 @@
1
+ import { Router, Request, Response } from "express";
2
+ import asyncHandler from "express-async-handler";
3
+
4
+ const router = Router();
5
+
6
+ /**
7
+ * @swagger
8
+ * /orders:
9
+ * get:
10
+ * summary: Get order home
11
+ * description: Returns the order home page.
12
+ * responses:
13
+ * 200:
14
+ * description: Order home page
15
+ */
16
+ router.get(
17
+ "/",
18
+ asyncHandler(async (req: Request, res: Response) => {
19
+ res.send("Order Home");
20
+ })
21
+ );
22
+
23
+ /**
24
+ * @swagger
25
+ * /orders/history:
26
+ * get:
27
+ * summary: Get order history
28
+ * description: Returns order history.
29
+ * responses:
30
+ * 200:
31
+ * description: Order history
32
+ */
33
+ router.get(
34
+ "/history",
35
+ asyncHandler(async (req: Request, res: Response) => {
36
+ res.send("Order History");
37
+ })
38
+ );
39
+
40
+ export = router;
@@ -0,0 +1,167 @@
1
+ import { Router, Request, Response } from "express";
2
+ import asyncHandler from "express-async-handler";
3
+
4
+ const router = Router();
5
+
6
+ // Array to store products (as a mock database)
7
+ const products = [
8
+ { id: 1, name: "Product1" },
9
+ { id: 2, name: "Product2" },
10
+ { id: 3, name: "Product3" },
11
+ { id: 4, name: "Product4" },
12
+ { id: 5, name: "Product5" },
13
+ { id: 6, name: "Product6" },
14
+ { id: 7, name: "Product7" },
15
+ { id: 8, name: "Product8" },
16
+ { id: 9, name: "Product9" },
17
+ ];
18
+
19
+ // CRUD Routes
20
+
21
+ /**
22
+ * @swagger
23
+ * /products:
24
+ * post:
25
+ * summary: Create an product
26
+ * description: Create a new product.
27
+ * requestBody:
28
+ * required: true
29
+ * content:
30
+ * application/json:
31
+ * schema:
32
+ * type: object
33
+ * properties:
34
+ * name:
35
+ * type: string
36
+ * description:
37
+ * type: string
38
+ * responses:
39
+ * 201:
40
+ * description: product created
41
+ */
42
+ router.post(
43
+ "/",
44
+ asyncHandler(async (req: Request, res: Response) => {
45
+ const product = { id: products.length + 1, name: req.body.name };
46
+ products.push(product);
47
+ res.status(201).json(product);
48
+ })
49
+ );
50
+
51
+ /**
52
+ * @swagger
53
+ * /products:
54
+ * get:
55
+ * summary: Get all products
56
+ * responses:
57
+ * 200:
58
+ * description: A list of products
59
+ */
60
+ router.get(
61
+ "/",
62
+ asyncHandler(async (req: Request, res: Response) => {
63
+ res.json(products);
64
+ })
65
+ );
66
+
67
+ /**
68
+ * @swagger
69
+ * /products/{id}:
70
+ * get:
71
+ * summary: Get an product by ID
72
+ * parameters:
73
+ * - name: id
74
+ * in: path
75
+ * required: true
76
+ * description: The ID of the product
77
+ * schema:
78
+ * type: integer
79
+ * responses:
80
+ * 200:
81
+ * description: An product object
82
+ * 404:
83
+ * description: product not found
84
+ */
85
+ router.get(
86
+ "/:id",
87
+ asyncHandler(async (req: Request, res: Response) => {
88
+ const product = products.find((i) => i.id === parseInt(req.params.id));
89
+ if (!product) res.status(404).send("product not found");
90
+ else res.json(product);
91
+ })
92
+ );
93
+
94
+ /**
95
+ * @swagger
96
+ * /products/{id}:
97
+ * put:
98
+ * summary: Update an product
99
+ * parameters:
100
+ * - name: id
101
+ * in: path
102
+ * required: true
103
+ * description: The ID of the product to update
104
+ * schema:
105
+ * type: integer
106
+ * requestBody:
107
+ * required: true
108
+ * content:
109
+ * application/json:
110
+ * schema:
111
+ * type: object
112
+ * properties:
113
+ * name:
114
+ * type: string
115
+ * description:
116
+ * type: string
117
+ * responses:
118
+ * 200:
119
+ * description: product updated
120
+ * 404:
121
+ * description: product not found
122
+ */
123
+ router.put(
124
+ "/:id",
125
+ asyncHandler(async (req: Request, res: Response) => {
126
+ const product = products.find((i) => i.id === parseInt(req.params.id));
127
+ if (!product) res.status(404).send("product not found");
128
+ else {
129
+ Object.assign(product, { id: req.body.id, name: req.body.name });
130
+ res.json(product);
131
+ }
132
+ })
133
+ );
134
+
135
+ /**
136
+ * @swagger
137
+ * /products/{id}:
138
+ * delete:
139
+ * summary: Delete an product
140
+ * parameters:
141
+ * - name: id
142
+ * in: path
143
+ * required: true
144
+ * description: The ID of the product to delete
145
+ * schema:
146
+ * type: integer
147
+ * responses:
148
+ * 204:
149
+ * description: product deleted
150
+ * 404:
151
+ * description: product not found
152
+ */
153
+ router.delete(
154
+ "/:id",
155
+ asyncHandler(async (req: Request, res: Response) => {
156
+ const productIndex = products.findIndex(
157
+ (i) => i.id === parseInt(req.params.id)
158
+ );
159
+ if (productIndex === -1) res.status(404).send("product not found");
160
+ else {
161
+ products.splice(productIndex, 1);
162
+ res.status(204).send();
163
+ }
164
+ })
165
+ );
166
+
167
+ export = router;
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+
3
+ export const CreateUserDto = z.object({
4
+ username: z.string().min(3),
5
+ email: z.string().email(),
6
+ password: z.string().min(6),
7
+ });
8
+
9
+ export type CreateUser = z.infer<typeof CreateUserDto>;
@@ -0,0 +1,81 @@
1
+ import { Router, Request, Response } from "express";
2
+ import asyncHandler from "express-async-handler";
3
+ import UserController from "./user.controller";
4
+
5
+ const router = Router();
6
+ const userController = new UserController();
7
+
8
+ /**
9
+ * @swagger
10
+ * /users:
11
+ * post:
12
+ * summary: Create a user
13
+ * description: Create a new user.
14
+ * requestBody:
15
+ * required: true
16
+ * content:
17
+ * application/json:
18
+ * schema:
19
+ * type: object
20
+ * required:
21
+ * - username
22
+ * - email
23
+ * - password
24
+ * properties:
25
+ * username:
26
+ * type: string
27
+ * email:
28
+ * type: string
29
+ * password:
30
+ * type: string
31
+ * example: # Sample object
32
+ * username: JessicaSmith
33
+ * email: william.howard.taft@my-own-personal-domain.com
34
+ * password: password123
35
+ * responses:
36
+ * 201:
37
+ * description: product created
38
+ */
39
+ router.post(
40
+ "/",
41
+ asyncHandler(async (req: Request, res: Response) => {
42
+ const result = await userController.createUser(req.body);
43
+ res.status(201).json({ message: result });
44
+ })
45
+ );
46
+
47
+ /**
48
+ * @swagger
49
+ * /user:
50
+ * get:
51
+ * summary: Get user home
52
+ * description: Returns the user home page.
53
+ * responses:
54
+ * 200:
55
+ * description: User home page
56
+ */
57
+ router.get(
58
+ "/",
59
+ asyncHandler(async (req: Request, res: Response) => {
60
+ res.send("User Home");
61
+ })
62
+ );
63
+
64
+ /**
65
+ * @swagger
66
+ * /user/profile:
67
+ * get:
68
+ * summary: Get user profile
69
+ * description: Returns user profile information.
70
+ * responses:
71
+ * 200:
72
+ * description: User profile information
73
+ */
74
+ router.get(
75
+ "/profile",
76
+ asyncHandler(async (req: Request, res: Response) => {
77
+ res.send("User Profile");
78
+ })
79
+ );
80
+
81
+ export = router;
@@ -0,0 +1,33 @@
1
+ import { Validate, ZodInput } from "ts-zod-decorators";
2
+ import { CreateUserDto, CreateUser } from "./dto/user.dto";
3
+ import { onError, rateLimit, throttle } from "utils-decorators";
4
+
5
+ function exceedHandler() {
6
+ throw new Error("Too much call in allowed window");
7
+ }
8
+
9
+ function errorHandler(e: Error): void {
10
+ console.error(e);
11
+ }
12
+
13
+ export default class UserController {
14
+ //constructor(private readonly userService: UserService) { }
15
+
16
+ // Throttle the createUser method to 1 request per 200 milliseconds
17
+ @rateLimit({
18
+ timeSpanMs: 60000,
19
+ allowedCalls: 300,
20
+ exceedHandler,
21
+ })
22
+ @onError({
23
+ func: errorHandler,
24
+ })
25
+ @Validate
26
+ public async createUser(
27
+ @ZodInput(CreateUserDto) data: CreateUser
28
+ ): Promise<string> {
29
+ // Here you can include logic to save user to database
30
+ console.log("Creating user:", data);
31
+ return "User created successfully";
32
+ }
33
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "experimentalDecorators": true,
11
+ "emitDecoratorMetadata": true
12
+ },
13
+ "include": ["src/**/*.ts", "src/**/*.js"],
14
+ "exclude": ["node_modules"]
15
+ }