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 +21 -0
- package/README.md +203 -0
- package/package.json +37 -0
- package/src/app.ts +95 -0
- package/src/config.ts +65 -0
- package/src/routers/index.ts +23 -0
- package/src/routers/orders/index.ts +40 -0
- package/src/routers/products/index.ts +167 -0
- package/src/routers/users/dto/user.dto.ts +9 -0
- package/src/routers/users/index.ts +81 -0
- package/src/routers/users/user.controller.ts +33 -0
- package/tsconfig.json +15 -0
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,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
|
+
}
|