codeweaver 3.1.3 → 4.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/README.md +56 -73
- package/package.json +23 -1
- package/src/config.ts +17 -15
- package/src/constants.ts +1 -0
- package/src/core/aws/api-gateway.ts +187 -0
- package/src/core/aws/basic-types.ts +147 -0
- package/src/core/aws/dynamodb.ts +187 -0
- package/src/core/aws/index.ts +9 -0
- package/src/core/aws/lambda.ts +199 -0
- package/src/core/aws/message-broker.ts +167 -0
- package/src/core/aws/message.ts +259 -0
- package/src/core/aws/s3.ts +136 -0
- package/src/core/aws/utilities.ts +44 -0
- package/src/core/cache/basic-types.ts +17 -0
- package/src/core/cache/decorator.ts +72 -0
- package/src/core/cache/index.ts +4 -0
- package/src/core/cache/memory-cache.class.ts +119 -0
- package/src/{utilities/cache/redis-cache.ts → core/cache/redis-cache.class.ts} +58 -10
- package/src/core/container/basic-types.ts +10 -0
- package/src/{utilities → core/container}/container.ts +7 -17
- package/src/core/container/index.ts +2 -0
- package/src/{utilities → core/error}/error-handling.ts +1 -65
- package/src/core/error/index.ts +3 -0
- package/src/core/error/response-error.ts +45 -0
- package/src/core/error/send-http-error.ts +15 -0
- package/src/core/file/file-helpers.ts +166 -0
- package/src/core/file/index.ts +1 -0
- package/src/{utilities → core/helpers}/assignment.ts +2 -2
- package/src/core/helpers/comparison.ts +86 -0
- package/src/{utilities → core/helpers}/conversion.ts +2 -2
- package/src/core/helpers/decorators.ts +316 -0
- package/src/core/helpers/format.ts +9 -0
- package/src/core/helpers/index.ts +7 -0
- package/src/core/helpers/range.ts +67 -0
- package/src/core/helpers/types.ts +3 -0
- package/src/core/logger/index.ts +4 -0
- package/src/{utilities/logger/logger.config.ts → core/logger/winston-logger.config.ts} +1 -1
- package/src/{utilities → core}/logger/winston-logger.service.ts +3 -3
- package/src/core/message-broker/bullmq/basic-types.ts +67 -0
- package/src/core/message-broker/bullmq/broker.ts +141 -0
- package/src/core/message-broker/bullmq/index.ts +3 -0
- package/src/core/message-broker/bullmq/queue.ts +58 -0
- package/src/core/message-broker/bullmq/worker.ts +68 -0
- package/src/core/message-broker/kafka/basic-types.ts +45 -0
- package/src/core/message-broker/kafka/consumer.ts +95 -0
- package/src/core/message-broker/kafka/index.ts +3 -0
- package/src/core/message-broker/kafka/producer.ts +113 -0
- package/src/core/message-broker/rabitmq/basic-types.ts +44 -0
- package/src/core/message-broker/rabitmq/channel.ts +95 -0
- package/src/core/message-broker/rabitmq/consumer.ts +94 -0
- package/src/core/message-broker/rabitmq/index.ts +4 -0
- package/src/core/message-broker/rabitmq/producer.ts +100 -0
- package/src/core/message-broker/utilities.ts +50 -0
- package/src/core/middlewares/basic-types.ts +39 -0
- package/src/core/middlewares/decorators.ts +244 -0
- package/src/core/middlewares/index.ts +3 -0
- package/src/core/middlewares/middlewares.ts +246 -0
- package/src/core/parallel/index.ts +3 -0
- package/src/{utilities → core}/parallel/parallel.ts +11 -1
- package/src/core/rate-limit/basic-types.ts +43 -0
- package/src/core/rate-limit/index.ts +4 -0
- package/src/core/rate-limit/memory-store.ts +65 -0
- package/src/core/rate-limit/rate-limit.ts +134 -0
- package/src/core/rate-limit/redis-store.ts +141 -0
- package/src/core/retry/basic-types.ts +21 -0
- package/src/core/retry/decorator.ts +139 -0
- package/src/core/retry/index.ts +2 -0
- package/src/main.ts +6 -8
- package/src/routers/orders/index.router.ts +5 -1
- package/src/routers/orders/order.controller.ts +54 -64
- package/src/routers/products/index.router.ts +2 -1
- package/src/routers/products/product.controller.ts +33 -68
- package/src/routers/users/index.router.ts +1 -1
- package/src/routers/users/user.controller.ts +25 -50
- package/src/utilities/cache/memory-cache.ts +0 -74
- /package/src/{utilities → core}/logger/base-logger.interface.ts +0 -0
- /package/src/{utilities → core}/logger/logger.service.ts +0 -0
- /package/src/{utilities → core}/parallel/chanel.ts +0 -0
- /package/src/{utilities → core}/parallel/worker-pool.ts +0 -0
package/README.md
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Codeweaver
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
**Codeweaver** is
|
|
5
|
+
**Codeweaver** is a lightweight framework and boilerplate built on top of `Express` and `TypeScript`. Its modular architecture for routers promotes scalability and organized development, making it easy to expand and maintain. Routers are automatically discovered and wired up through a conventional folder structure, simplifying project organization and reducing boilerplate. Routers can be nested, allowing you to compose complex route trees by placing sub-routers inside parent router folders.
|
|
6
6
|
|
|
7
7
|
## Features and Technologies Used
|
|
8
8
|
|
|
9
|
-
- **Node.js**
|
|
10
|
-
- **Express**: A lightweight web framework for building server-side applications in Node.js.
|
|
11
|
-
- **TypeScript**: Adds strong typing for enhanced development experience and reduced runtime errors.
|
|
12
9
|
- **Modular Router Structure**: Automates importing and mounting routers, ensuring a clean separation of endpoints and logic for easier scalability.
|
|
13
|
-
- **Dependency resolver**: A simple dependency resolver that uses a lightweight container to manage and inject dependencies at runtime.
|
|
14
10
|
- **Swagger Integration**: Automatically generates interactive API documentation, facilitating easier understanding of available endpoints for developers and consumers.
|
|
15
|
-
- **
|
|
16
|
-
- **Zod**: Implements schema validation for input data.
|
|
17
|
-
- **Utils-decorators**: A collection of middleware utilities utils-decorators (throttling, caching, and error handling) designed to strengthen application resilience.
|
|
11
|
+
- **Dependency resolver**: A simple dependency resolver that uses a lightweight container to manage and inject dependencies at runtime.
|
|
18
12
|
- **Logger**: A Winston-based logger (with LogForm) that provides scalable, leveled logging, structured JSON output, and pluggable transports (console and file)
|
|
13
|
+
- **AWS integrations**: API Gateway, Lambda, S3, SNS, SES, SQS, and DynamoDB adapters
|
|
14
|
+
- **IO**: Reading, writing JSON and YAML
|
|
15
|
+
- **Messaging**: Kafka, RabbitMQ, BullMQ adapters
|
|
16
|
+
- **Parallelism**: Channel, Worker-pool
|
|
17
|
+
- **Decorators and Middlewares**:
|
|
18
|
+
- Caching: Memory and Redis backends
|
|
19
|
+
- Rate limiting, Retry, Debounce, Timeout
|
|
20
|
+
- Guard (authentication/authorization)
|
|
21
|
+
- Before/After hooks
|
|
22
|
+
- Memoization
|
|
23
|
+
- Logging: Winston-based, LogForm, structured JSON, pluggable transports
|
|
24
|
+
- Error handling: Centralized error types and handlers
|
|
19
25
|
|
|
20
26
|
Here's a revised Installation section that explicitly supports pnpm in addition to npm. I kept the formatting and steps intact, adding clear pnpm equivalents.
|
|
21
27
|
|
|
@@ -47,6 +53,14 @@ To get started with the project, follow these steps:
|
|
|
47
53
|
|
|
48
54
|
3. **Run the application**:
|
|
49
55
|
|
|
56
|
+
Using pnpm:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pnpm start
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Using npm:
|
|
63
|
+
|
|
50
64
|
```bash
|
|
51
65
|
npm start
|
|
52
66
|
```
|
|
@@ -55,6 +69,15 @@ To get started with the project, follow these steps:
|
|
|
55
69
|
|
|
56
70
|
5. **Build**: Compile the TypeScript files for the production environment:
|
|
57
71
|
|
|
72
|
+
Using pnpm:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pnpm run build
|
|
76
|
+
pnpm run serve
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Using npm:
|
|
80
|
+
|
|
58
81
|
```bash
|
|
59
82
|
npm run build
|
|
60
83
|
npm run serve
|
|
@@ -101,7 +124,7 @@ Example of a basic router:
|
|
|
101
124
|
import { Router, Request, Response } from "express";
|
|
102
125
|
import asyncHandler from "express-async-handler";
|
|
103
126
|
import UserController from "./user.controller";
|
|
104
|
-
import { resolve } from "@/
|
|
127
|
+
import { resolve } from "@/core/container";
|
|
105
128
|
|
|
106
129
|
const router = Router();
|
|
107
130
|
const userController = resolve(UserController);
|
|
@@ -211,28 +234,23 @@ Here’s a brief breakdown of key components used in the `UserController`:
|
|
|
211
234
|
|
|
212
235
|
```typescript
|
|
213
236
|
import { UserCreationDto, UserDto, ZodUserDto } from "./dto/user.dto";
|
|
214
|
-
import {
|
|
215
|
-
import {
|
|
216
|
-
import { convert, stringToInteger } from "@/utilities/conversion";
|
|
237
|
+
import { ResponseError } from "@/core/error";
|
|
238
|
+
import { convert, stringToInteger } from "@/core/helpers";
|
|
217
239
|
import { config } from "@/config";
|
|
218
240
|
import { users } from "@/db";
|
|
219
241
|
import { User, ZodUser } from "@/entities/user.entity";
|
|
220
|
-
import { MapAsyncCache } from "@/
|
|
221
|
-
import { Injectable } from "@/
|
|
222
|
-
import { parallelMap } from "@/
|
|
242
|
+
import { Invalidate, MapAsyncCache, Memoize } from "@/core/cache";
|
|
243
|
+
import { Injectable } from "@/core/container";
|
|
244
|
+
import { parallelMap } from "@/core/parallel";
|
|
245
|
+
import { ErrorHandler, LogMethod, Timeout } from "@/core/middlewares";
|
|
246
|
+
import { RateLimit } from "@/core/rate-limit";
|
|
223
247
|
|
|
224
|
-
function
|
|
225
|
-
const message = "Too much call in allowed window";
|
|
226
|
-
throw new ResponseError(message, 429);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function invalidInputHandler(e: ResponseError) {
|
|
248
|
+
async function invalidInputHandler(e: ResponseError) {
|
|
230
249
|
const message = "Invalid input";
|
|
231
250
|
throw new ResponseError(message, 400, e?.message);
|
|
232
251
|
}
|
|
233
252
|
|
|
234
253
|
const usersCache = new MapAsyncCache<UserDto[]>(config.cacheSize);
|
|
235
|
-
const userCache = new MapAsyncCache<UserDto>(config.cacheSize);
|
|
236
254
|
|
|
237
255
|
@Injectable()
|
|
238
256
|
/**
|
|
@@ -243,9 +261,7 @@ const userCache = new MapAsyncCache<UserDto>(config.cacheSize);
|
|
|
243
261
|
export default class UserController {
|
|
244
262
|
// constructor(private readonly userService: UserService) { }
|
|
245
263
|
|
|
246
|
-
@
|
|
247
|
-
func: invalidInputHandler,
|
|
248
|
-
})
|
|
264
|
+
@ErrorHandler(invalidInputHandler)
|
|
249
265
|
/**
|
|
250
266
|
* Validates a string ID and converts it to a number.
|
|
251
267
|
*
|
|
@@ -256,9 +272,7 @@ export default class UserController {
|
|
|
256
272
|
return stringToInteger(id);
|
|
257
273
|
}
|
|
258
274
|
|
|
259
|
-
@
|
|
260
|
-
func: invalidInputHandler,
|
|
261
|
-
})
|
|
275
|
+
@ErrorHandler(invalidInputHandler)
|
|
262
276
|
/**
|
|
263
277
|
* Validates and creates a new User from the given DTO.
|
|
264
278
|
*
|
|
@@ -269,11 +283,8 @@ export default class UserController {
|
|
|
269
283
|
return await convert(user, ZodUser);
|
|
270
284
|
}
|
|
271
285
|
|
|
272
|
-
@
|
|
273
|
-
|
|
274
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
275
|
-
exceedHandler,
|
|
276
|
-
})
|
|
286
|
+
@Invalidate(usersCache)
|
|
287
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
277
288
|
/**
|
|
278
289
|
* Create a new user
|
|
279
290
|
* @param {User} user - User creation data validated by Zod schema
|
|
@@ -283,42 +294,26 @@ export default class UserController {
|
|
|
283
294
|
*/
|
|
284
295
|
public async create(user: User): Promise<void> {
|
|
285
296
|
users.push(user);
|
|
286
|
-
await usersCache.delete("key");
|
|
287
297
|
}
|
|
288
298
|
|
|
289
|
-
@
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
expirationTimeMs: config.memoizeTime,
|
|
293
|
-
})
|
|
294
|
-
@timeout(config.timeout)
|
|
295
|
-
@rateLimit({
|
|
296
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
297
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
298
|
-
exceedHandler,
|
|
299
|
-
})
|
|
299
|
+
@Memoize(usersCache)
|
|
300
|
+
@Timeout(config.timeout)
|
|
301
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
300
302
|
/**
|
|
301
303
|
* Get all users
|
|
302
304
|
* @returns {Promise<UserDto[]>} List of users with hidden password fields
|
|
303
305
|
* @throws {ResponseError} 500 - When rate limit exceeded
|
|
304
306
|
*/
|
|
305
|
-
public async getAll(): Promise<UserDto[]> {
|
|
306
|
-
return await parallelMap(
|
|
307
|
-
|
|
308
|
-
|
|
307
|
+
public async getAll(signal?: AbortSignal): Promise<(UserDto | undefined)[]> {
|
|
308
|
+
return await parallelMap(users, async (user) =>
|
|
309
|
+
signal?.aborted == false
|
|
310
|
+
? await convert<User, UserDto>(user!, ZodUserDto)
|
|
311
|
+
: null
|
|
309
312
|
);
|
|
310
313
|
}
|
|
311
314
|
|
|
312
|
-
@
|
|
313
|
-
|
|
314
|
-
keyResolver: (id: number) => id.toString(),
|
|
315
|
-
expirationTimeMs: config.memoizeTime,
|
|
316
|
-
})
|
|
317
|
-
@rateLimit({
|
|
318
|
-
timeSpanMs: config.rateLimitTimeSpan,
|
|
319
|
-
allowedCalls: config.rateLimitAllowedCalls,
|
|
320
|
-
exceedHandler,
|
|
321
|
-
})
|
|
315
|
+
@Memoize(usersCache)
|
|
316
|
+
@RateLimit(config.rateLimitTimeSpan, config.rateLimitAllowedCalls)
|
|
322
317
|
/**
|
|
323
318
|
* Get user by ID
|
|
324
319
|
* @param {number} id - User ID as string
|
|
@@ -329,7 +324,7 @@ export default class UserController {
|
|
|
329
324
|
public async get(id: number): Promise<UserDto> {
|
|
330
325
|
const user = users.find((user) => user.id === id);
|
|
331
326
|
if (user == null) {
|
|
332
|
-
throw new ResponseError("
|
|
327
|
+
throw new ResponseError("User not found");
|
|
333
328
|
}
|
|
334
329
|
return convert(user, ZodUserDto);
|
|
335
330
|
}
|
|
@@ -338,18 +333,6 @@ export default class UserController {
|
|
|
338
333
|
|
|
339
334
|
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.
|
|
340
335
|
|
|
341
|
-
### Async Handlers
|
|
342
|
-
|
|
343
|
-
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.
|
|
344
|
-
|
|
345
|
-
### API Documentation
|
|
346
|
-
|
|
347
|
-
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.
|
|
348
|
-
|
|
349
|
-
### Decorators
|
|
350
|
-
|
|
351
|
-
To prevent abuse of your API, you can utilize throttling, caching, and error handling decorators from the `utils-decorators` package. This package provides decorators that can be applied directly to your service and controller classes.
|
|
352
|
-
|
|
353
336
|
### Contributing
|
|
354
337
|
|
|
355
338
|
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeweaver",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"main": "src/main.ts",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codeweaver": "command.js"
|
|
@@ -26,13 +26,30 @@
|
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"description": "An unopinionated microframework built with Express, TypeScript, Zod, Swagger",
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@aws-sdk/client-api-gateway": "^3.943.0",
|
|
30
|
+
"@aws-sdk/client-dynamodb": "^3.943.0",
|
|
31
|
+
"@aws-sdk/client-lambda": "^3.942.0",
|
|
32
|
+
"@aws-sdk/client-s3": "^3.940.0",
|
|
33
|
+
"@aws-sdk/client-sesv2": "^3.943.0",
|
|
34
|
+
"@aws-sdk/client-sns": "^3.943.0",
|
|
35
|
+
"@aws-sdk/client-sqs": "^3.943.0",
|
|
36
|
+
"@aws-sdk/lib-dynamodb": "^3.943.0",
|
|
37
|
+
"@aws-sdk/s3-request-presigner": "^3.940.0",
|
|
38
|
+
"amqplib": "^0.10.9",
|
|
39
|
+
"bullmq": "^5.65.1",
|
|
29
40
|
"cors": "^2.8.5",
|
|
30
41
|
"dotenv": "^17.2.3",
|
|
42
|
+
"drizzle-orm": "^0.45.0",
|
|
31
43
|
"express": "^5.1.0",
|
|
32
44
|
"express-async-handler": "^1.2.0",
|
|
33
45
|
"express-winston": "^4.2.0",
|
|
46
|
+
"helmet": "^8.1.0",
|
|
34
47
|
"ioredis": "^5.8.2",
|
|
48
|
+
"js-yaml": "^4.1.1",
|
|
49
|
+
"jsonwebtoken": "^9.0.2",
|
|
50
|
+
"kafkajs": "^2.2.4",
|
|
35
51
|
"logform": "^2.7.0",
|
|
52
|
+
"passport": "^0.7.0",
|
|
36
53
|
"reflect-metadata": "^0.2.2",
|
|
37
54
|
"swagger-jsdoc": "^3.7.0",
|
|
38
55
|
"utils-decorators": "^2.10.0",
|
|
@@ -41,11 +58,16 @@
|
|
|
41
58
|
"zod": "^4.0.14"
|
|
42
59
|
},
|
|
43
60
|
"devDependencies": {
|
|
61
|
+
"@aws-sdk/types": "^3.936.0",
|
|
44
62
|
"@types/cors": "^2.8.19",
|
|
45
63
|
"@types/express": "^5.0.3",
|
|
64
|
+
"@types/js-yaml": "^4.0.9",
|
|
65
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
46
66
|
"@types/node": "^24.1.0",
|
|
67
|
+
"@types/passport": "^1.0.17",
|
|
47
68
|
"copyfiles": "^2.4.1",
|
|
48
69
|
"cross-env": "^10.0.0",
|
|
70
|
+
"drizzle-kit": "^0.31.8",
|
|
49
71
|
"nodemon": "^3.1.10",
|
|
50
72
|
"swagger-ui-express": "^5.0.1",
|
|
51
73
|
"ts-node": "^10.9.2",
|
package/src/config.ts
CHANGED
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
port,
|
|
8
8
|
cacheSize,
|
|
9
9
|
http,
|
|
10
|
-
|
|
10
|
+
debounceTimeSpan,
|
|
11
|
+
} from "@/constants";
|
|
11
12
|
import { SwaggerOptions } from "./swagger-options";
|
|
12
|
-
import { stringToBoolean } from "
|
|
13
|
-
import { logger } from "./utilities/logger/logger.config";
|
|
13
|
+
import { stringToBoolean } from "@/core/helpers/conversion";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Main application configuration
|
|
@@ -20,16 +20,18 @@ import { logger } from "./utilities/logger/logger.config";
|
|
|
20
20
|
* @property {SwaggerOptions} swaggerOptions - Swagger configuration
|
|
21
21
|
*/
|
|
22
22
|
interface Config {
|
|
23
|
-
devMode: boolean;
|
|
24
|
-
http: string;
|
|
25
|
-
port: number;
|
|
26
|
-
swagger: boolean;
|
|
27
|
-
swaggerOptions: SwaggerOptions;
|
|
28
|
-
timeout: number;
|
|
29
|
-
rateLimitTimeSpan: number;
|
|
30
|
-
rateLimitAllowedCalls: number;
|
|
31
|
-
memoizeTime: number;
|
|
32
|
-
cacheSize: number;
|
|
23
|
+
readonly devMode: boolean;
|
|
24
|
+
readonly http: string;
|
|
25
|
+
readonly port: number;
|
|
26
|
+
readonly swagger: boolean;
|
|
27
|
+
readonly swaggerOptions: SwaggerOptions;
|
|
28
|
+
readonly timeout: number;
|
|
29
|
+
readonly rateLimitTimeSpan: number;
|
|
30
|
+
readonly rateLimitAllowedCalls: number;
|
|
31
|
+
readonly memoizeTime: number;
|
|
32
|
+
readonly cacheSize: number;
|
|
33
|
+
readonly jwtSecretKey: string;
|
|
34
|
+
readonly debounceTimeSpan: number;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export const config: Config = {
|
|
@@ -65,6 +67,6 @@ export const config: Config = {
|
|
|
65
67
|
Number(process.env.RATE_LIMIT_ALLOWED_CALLS) || rateLimitAllowedCalls,
|
|
66
68
|
memoizeTime: Number(process.env.MEMOIZE_TIME) || memoizeTime,
|
|
67
69
|
cacheSize: Number(process.env.CACHE_SIZE) || cacheSize,
|
|
70
|
+
jwtSecretKey: process.env.JWT_SECRET_KEY!,
|
|
71
|
+
debounceTimeSpan: Number(process.env.DEBOUNCE_TIME_SPAN) || debounceTimeSpan,
|
|
68
72
|
};
|
|
69
|
-
|
|
70
|
-
export const container = { logger };
|
package/src/constants.ts
CHANGED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {
|
|
2
|
+
APIGatewayClient,
|
|
3
|
+
CreateRestApiCommand,
|
|
4
|
+
GetRestApisCommand,
|
|
5
|
+
CreateResourceCommand,
|
|
6
|
+
PutMethodCommand,
|
|
7
|
+
PutIntegrationCommand,
|
|
8
|
+
CreateDeploymentCommand,
|
|
9
|
+
GetResourcesCommand,
|
|
10
|
+
} from "@aws-sdk/client-api-gateway";
|
|
11
|
+
import { LambdaClient, AddPermissionCommand } from "@aws-sdk/client-lambda";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Comprehensive TypeScript library for managing AWS API Gateway REST APIs
|
|
15
|
+
* and linking them to AWS Lambda functions.
|
|
16
|
+
* Supports full lifecycle: API creation, resource/method setup,
|
|
17
|
+
* Lambda integration, permissions, and deployment.
|
|
18
|
+
*/
|
|
19
|
+
export class APIGateway {
|
|
20
|
+
public apiGateway!: APIGatewayClient;
|
|
21
|
+
public lambda!: LambdaClient;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates an instance of APIGatewayLambdaLinker.
|
|
25
|
+
* @param options - Configuration options.
|
|
26
|
+
* @param [options.region=us-east-1] - AWS region for operations.
|
|
27
|
+
*/
|
|
28
|
+
public constructor(region?: string, ...args: any[]) {
|
|
29
|
+
if (region == null) {
|
|
30
|
+
this.apiGateway = new APIGatewayClient({ region, ...args });
|
|
31
|
+
this.lambda = new LambdaClient({ region, ...args });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new REST API in API Gateway.
|
|
37
|
+
* @param name - Unique name for the REST API.
|
|
38
|
+
* @param [description] - Optional description of the API.
|
|
39
|
+
* @returns Promise resolving to the created RestApi object containing API ID and details.
|
|
40
|
+
* @throws Will throw if API creation fails (e.g., duplicate name, permissions).
|
|
41
|
+
*/
|
|
42
|
+
public async createRestApi(name: string, description?: string): Promise<any> {
|
|
43
|
+
const command = new CreateRestApiCommand({
|
|
44
|
+
name,
|
|
45
|
+
description,
|
|
46
|
+
endpointConfiguration: { types: ["REGIONAL"] },
|
|
47
|
+
});
|
|
48
|
+
return this.apiGateway.send(command);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Lists all REST APIs in the current region.
|
|
53
|
+
* @returns Promise resolving to array of RestApi objects or empty array.
|
|
54
|
+
*/
|
|
55
|
+
public async listRestApis(): Promise<any[]> {
|
|
56
|
+
const command = new GetRestApisCommand({});
|
|
57
|
+
const response = await this.apiGateway.send(command);
|
|
58
|
+
return response.items || [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves all resources for a specific REST API.
|
|
63
|
+
* @param restApiId - The ID of the REST API.
|
|
64
|
+
* @returns Promise resolving to array of Resource objects or empty array.
|
|
65
|
+
*/
|
|
66
|
+
public async getResources(restApiId: string): Promise<any[]> {
|
|
67
|
+
const command = new GetResourcesCommand({ restApiId });
|
|
68
|
+
const response = await this.apiGateway.send(command);
|
|
69
|
+
return response.items || [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new resource (path segment) under a parent resource.
|
|
74
|
+
* Use root resource (path '/') as parentId for top-level paths like '/items'.
|
|
75
|
+
* @param restApiId - The ID of the REST API.
|
|
76
|
+
* @param parentId - ID of the parent resource.
|
|
77
|
+
* @param pathPart - Path segment name (e.g., 'items' for '/items').
|
|
78
|
+
* @returns Promise resolving to the created Resource object.
|
|
79
|
+
*/
|
|
80
|
+
public async createResource(
|
|
81
|
+
restApiId: string,
|
|
82
|
+
parentId: string,
|
|
83
|
+
pathPart: string
|
|
84
|
+
): Promise<any> {
|
|
85
|
+
const command = new CreateResourceCommand({
|
|
86
|
+
restApiId,
|
|
87
|
+
parentId,
|
|
88
|
+
pathPart,
|
|
89
|
+
});
|
|
90
|
+
return this.apiGateway.send(command);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Creates or updates an HTTP method on a resource.
|
|
95
|
+
* @param restApiId - The ID of the REST API.
|
|
96
|
+
* @param resourceId - The ID of the resource.
|
|
97
|
+
* @param httpMethod - HTTP method (GET, POST, PUT, DELETE, etc.).
|
|
98
|
+
* @param [authorizationType="NONE"] - Authorization type (NONE, AWS_IAM, CUSTOM, COGNITO_USER_POOLS).
|
|
99
|
+
* @returns Promise resolving to the method configuration.
|
|
100
|
+
*/
|
|
101
|
+
public async putMethod(
|
|
102
|
+
restApiId: string,
|
|
103
|
+
resourceId: string,
|
|
104
|
+
httpMethod: string,
|
|
105
|
+
authorizationType: string = "NONE"
|
|
106
|
+
): Promise<any> {
|
|
107
|
+
const command = new PutMethodCommand({
|
|
108
|
+
restApiId,
|
|
109
|
+
resourceId,
|
|
110
|
+
httpMethod,
|
|
111
|
+
authorizationType,
|
|
112
|
+
apiKeyRequired: false,
|
|
113
|
+
});
|
|
114
|
+
return this.apiGateway.send(command);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Links a Lambda function as the backend integration for an HTTP method using AWS_PROXY integration.
|
|
119
|
+
* @param restApiId - The ID of the REST API.
|
|
120
|
+
* @param resourceId - The ID of the resource.
|
|
121
|
+
* @param httpMethod - HTTP method (must match previously created method).
|
|
122
|
+
* @param lambdaArn - Full ARN of the Lambda function.
|
|
123
|
+
* @returns Promise resolving to the integration configuration.
|
|
124
|
+
*/
|
|
125
|
+
public async putIntegration(
|
|
126
|
+
restApiId: string,
|
|
127
|
+
resourceId: string,
|
|
128
|
+
httpMethod: string,
|
|
129
|
+
lambdaArn: string
|
|
130
|
+
): Promise<any> {
|
|
131
|
+
const region = this.apiGateway.config.region || "us-east-1";
|
|
132
|
+
const command = new PutIntegrationCommand({
|
|
133
|
+
restApiId,
|
|
134
|
+
resourceId,
|
|
135
|
+
httpMethod,
|
|
136
|
+
type: "AWS_PROXY",
|
|
137
|
+
integrationHttpMethod: "POST",
|
|
138
|
+
uri: `arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations`,
|
|
139
|
+
});
|
|
140
|
+
return this.apiGateway.send(command);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Adds Lambda execution permission for API Gateway to invoke the function.
|
|
145
|
+
* Required for Lambda integration to work.
|
|
146
|
+
* @param functionName - Name or ARN of the Lambda function.
|
|
147
|
+
* @param statementId - Unique ID for this permission statement.
|
|
148
|
+
* @param restApiId - The ID of the REST API.
|
|
149
|
+
* @param region - AWS region.
|
|
150
|
+
* @param accountId - AWS account ID (12 digits).
|
|
151
|
+
* @returns Promise resolving to the permission policy statement.
|
|
152
|
+
*/
|
|
153
|
+
public async addLambdaPermission(
|
|
154
|
+
functionName: string,
|
|
155
|
+
statementId: string,
|
|
156
|
+
restApiId: string,
|
|
157
|
+
region: string,
|
|
158
|
+
accountId: string
|
|
159
|
+
): Promise<any> {
|
|
160
|
+
const sourceArn = `arn:aws:execute-api:${region}:${accountId}:${restApiId}/*/*/*`;
|
|
161
|
+
const command = new AddPermissionCommand({
|
|
162
|
+
FunctionName: functionName,
|
|
163
|
+
StatementId: statementId,
|
|
164
|
+
Action: "lambda:InvokeFunction",
|
|
165
|
+
Principal: "apigateway.amazonaws.com",
|
|
166
|
+
SourceArn: sourceArn,
|
|
167
|
+
});
|
|
168
|
+
return this.lambda.send(command);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Deploys the REST API to a specific stage, making it accessible via invoke URL.
|
|
173
|
+
* @param restApiId - The ID of the REST API.
|
|
174
|
+
* @param stageName - Stage name (dev, prod, test, etc.).
|
|
175
|
+
* @returns Promise resolving to the deployment object.
|
|
176
|
+
*/
|
|
177
|
+
public async createDeployment(
|
|
178
|
+
restApiId: string,
|
|
179
|
+
stageName: string
|
|
180
|
+
): Promise<any> {
|
|
181
|
+
const command = new CreateDeploymentCommand({
|
|
182
|
+
restApiId,
|
|
183
|
+
stageName,
|
|
184
|
+
});
|
|
185
|
+
return this.apiGateway.send(command);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Runtime } from "@aws-sdk/client-lambda";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lambda function creation parameters
|
|
5
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
|
|
6
|
+
*/
|
|
7
|
+
export interface LambdaCreateParams {
|
|
8
|
+
/** Lambda function name to create or update (if already exists) in AWS */
|
|
9
|
+
functionName: string;
|
|
10
|
+
/**
|
|
11
|
+
* IAM role ARN
|
|
12
|
+
*
|
|
13
|
+
* @default arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
|
|
14
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/lambda-permissions.html
|
|
15
|
+
*/
|
|
16
|
+
roleArn: string;
|
|
17
|
+
/**
|
|
18
|
+
* Path to zip file containing lambda code
|
|
19
|
+
*
|
|
20
|
+
* @default index.zip
|
|
21
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/lambda-zip-file-format.html
|
|
22
|
+
*/
|
|
23
|
+
zipFilePath: string;
|
|
24
|
+
/**
|
|
25
|
+
* Lambda handler
|
|
26
|
+
*
|
|
27
|
+
* @default index.handler
|
|
28
|
+
*/
|
|
29
|
+
handler: string;
|
|
30
|
+
/**
|
|
31
|
+
* Lambda runtime
|
|
32
|
+
*
|
|
33
|
+
* @default Runtime.nodejs18x
|
|
34
|
+
*/
|
|
35
|
+
runtime?: Runtime;
|
|
36
|
+
/**
|
|
37
|
+
* Timeout in seconds
|
|
38
|
+
*
|
|
39
|
+
* @default 15
|
|
40
|
+
*/
|
|
41
|
+
timeout?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Memory size in MB
|
|
44
|
+
*
|
|
45
|
+
* @default 128
|
|
46
|
+
*/
|
|
47
|
+
memorySize?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Environment variables
|
|
50
|
+
*
|
|
51
|
+
* @default {}
|
|
52
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/lambda-environment-variables.html
|
|
53
|
+
*/
|
|
54
|
+
environment?: Record<string, string>;
|
|
55
|
+
/**
|
|
56
|
+
* AWS region
|
|
57
|
+
|
|
58
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-regions.html
|
|
59
|
+
*/
|
|
60
|
+
region?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Parameters for uploading a file to an S3 bucket
|
|
65
|
+
*/
|
|
66
|
+
export interface UploadParams {
|
|
67
|
+
bucket: string;
|
|
68
|
+
/** Key of the object to create in the bucket */
|
|
69
|
+
key: string;
|
|
70
|
+
filePath: string;
|
|
71
|
+
/**
|
|
72
|
+
* Additional parameters to pass to the PutObjectCommand
|
|
73
|
+
* @see https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html
|
|
74
|
+
*/
|
|
75
|
+
extraArgs?: Record<string, any>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Parameters for downloading an object from an S3 bucket
|
|
80
|
+
*/
|
|
81
|
+
export interface DownloadParams {
|
|
82
|
+
bucket: string;
|
|
83
|
+
/** Key of the object to create in the bucket */
|
|
84
|
+
key: string;
|
|
85
|
+
destPath: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Email message configuration for SES.
|
|
90
|
+
*/
|
|
91
|
+
export interface EmailMessage {
|
|
92
|
+
/** Recipient email addresses. */
|
|
93
|
+
to: string[];
|
|
94
|
+
/** Optional CC recipient email addresses. */
|
|
95
|
+
cc?: string[];
|
|
96
|
+
/** Optional BCC recipient email addresses. */
|
|
97
|
+
bcc?: string[];
|
|
98
|
+
/** Email subject line. */
|
|
99
|
+
subject: string;
|
|
100
|
+
/** Plain text body content. */
|
|
101
|
+
textBody?: string;
|
|
102
|
+
/** HTML body content. */
|
|
103
|
+
htmlBody?: string;
|
|
104
|
+
/** Sender email address (must be SES verified). */
|
|
105
|
+
from: string;
|
|
106
|
+
/** Optional reply-to email addresses. */
|
|
107
|
+
replyTo?: string[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* SMS message configuration for SNS.
|
|
112
|
+
*/
|
|
113
|
+
export interface SMSMessage {
|
|
114
|
+
/** Phone number in E.164 format (e.g., '+15551234567'). */
|
|
115
|
+
phoneNumber: string;
|
|
116
|
+
/** Message content (max 160 characters for standard SMS). */
|
|
117
|
+
message: string;
|
|
118
|
+
/** Optional message attributes for customization. */
|
|
119
|
+
attributes?: Record<string, any>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Define a strict, explicit cache type
|
|
123
|
+
export interface PushPlatformArns {
|
|
124
|
+
/**
|
|
125
|
+
* ARN for the APNS platform application
|
|
126
|
+
* @see https://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html
|
|
127
|
+
*/
|
|
128
|
+
apns?: string;
|
|
129
|
+
/**
|
|
130
|
+
* ARN for the APNS_SANDBOX platform application
|
|
131
|
+
* @see https://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html
|
|
132
|
+
*/
|
|
133
|
+
apns_sandbox?: string;
|
|
134
|
+
/**
|
|
135
|
+
* ARN for the FCM platform application
|
|
136
|
+
* @see https://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html
|
|
137
|
+
*/
|
|
138
|
+
fcm?: string;
|
|
139
|
+
/**
|
|
140
|
+
* ARN for the ADM platform application
|
|
141
|
+
* @see https://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html
|
|
142
|
+
*/
|
|
143
|
+
adm?: string;
|
|
144
|
+
|
|
145
|
+
/** Additional platforms as needed in a controlled manner */
|
|
146
|
+
[platform: string]: string | undefined;
|
|
147
|
+
}
|