minimonolith 0.11.0 → 0.12.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 CHANGED
@@ -1,3 +1,131 @@
1
1
  # minimonolith
2
2
 
3
- [![codecov](https://codecov.io/gh/DeepHackDev/minimonolith-lib/branch/master/graph/badge.svg?token=ORFNKKJRSE)](https://codecov.io/gh/DeepHackDev/minimonolith-lib)
3
+ `minimonolith` is a lightweight library designed to help you build serverless APIs using AWS Lambda, with a focus on simplicity and ease of use. The library provides a straightforward structure to organize your API's services, methods, validation, and models while handling common tasks like database connection and request validation.
4
+
5
+ In addition to its simplicity, `minimonolith` enables seamless inter-service communication within your API. This allows services to call one another's functionality without directly importing them, fostering a modular design. For example, you can call the get method of the todo service from the todoList service using SERVICES.todo.get({ id }). By registering services within the API, you can easily call their methods from other services, which not only promotes a clean architecture but also paves the way for future support of automated end-to-end testing.
6
+
7
+ ## Project Structure
8
+
9
+ A typical project using `minimonolith` will have the following structure:
10
+
11
+ ```go
12
+ .
13
+ ├── package.json
14
+ ├── .gitignore
15
+ ├── .env
16
+ ├── server.js // For local development
17
+ ├── index.js // Root of the code in a deployed AWS Lambda
18
+ └── service1
19
+ ├── index.js // Service1 method handlers are declared here
20
+ ├── model.js // Optional: Sequelize model for Service1 is declared here
21
+ └── method1
22
+ ├── index.js // Service1 Method1 handler is declared here
23
+ └── valid.js // Optional: Method1 handler body validation, if not empty
24
+ ```
25
+
26
+ ## Example Project
27
+
28
+ Here's an example project using `minimonolith`:
29
+
30
+ ```go
31
+ .
32
+ ├── package.json
33
+ ├── .gitignore
34
+ ├── .env
35
+ ├── server.js // For local development
36
+ ├── index.js // Root of the code in a deployed AWS Lambda
37
+ └── todo
38
+ ├── index.js // Todo method handlers are declared here
39
+ ├── model.js // Todo Sequelize model is declared here
40
+ └── get
41
+ ├── index.js // /todo/get/ handler
42
+ └── valid.js // /todo/get/index.js body validation
43
+ ```
44
+
45
+ ### server.js
46
+
47
+ This file is used for local development. It runs a local server using `minimonolith`'s `runLambdaServer` function:
48
+
49
+ ```js
50
+ // server.js
51
+ import { runLambdaServer, loadEnvFile } from 'minimonolith';
52
+ loadEnvFile(); // .env file loaded by default
53
+
54
+ const lambdaRootFile = await import('./index.js');
55
+ runLambdaServer(lambdaRootFile.lambdaHandler, 8080);
56
+ ```
57
+
58
+ ### index.js
59
+
60
+ This file serves as the root of the code in a deployed AWS Lambda:
61
+
62
+ ```js
63
+ // index.js
64
+ 'use strict';
65
+
66
+ import { createAPI } from 'minimonolith';
67
+
68
+ const API = createAPI();
69
+
70
+ await API.registerHealthService(); // Adds health check service to route /
71
+ await API.registerService('todo'); // Registers all /todo routes with respective method handlers
72
+ await API.registerDatabaseService(); // Sets up the database connection
73
+
74
+ export const lambdaHandler = await API.handler(); // Synchronizes Sequelize models with the database
75
+ ```
76
+
77
+ ### todo/index.js
78
+
79
+ Here, we declare the method handlers for the `todo` service:
80
+
81
+ ```js
82
+ // todo/index.js
83
+ export const methods = ['getAll', 'get:id', 'post', 'patch:id', 'delete:id'];
84
+ ```
85
+
86
+ ### todo/model.js
87
+
88
+ In this file, we define a Sequelize model for the `todo` service:
89
+
90
+ ```js
91
+ // todo/model.js
92
+ export const modelSchema = serviceName => (orm, types) => {
93
+ const schema = orm.define(serviceName, {
94
+ name: {
95
+ type: types.STRING,
96
+ allowNull: false
97
+ },
98
+ });
99
+
100
+ schema.associate = MODELS => {}; // e.g. MODELS.todo.belongsTo(MODELS.todoList, {...});
101
+
102
+ return schema;
103
+ };
104
+ ```
105
+
106
+ ### todo/get/index.js
107
+
108
+ This file contains the `get:id` method handler for the `todo` service. It retrieves a todo item by its ID:
109
+
110
+ ```js
111
+ // todo/get/index.js
112
+ export default async ({ body, MODELS }) => {
113
+ const id = parseInt(body.id);
114
+ return await MODELS.todo.findOne({ where: { id } });
115
+ }
116
+ ```
117
+
118
+ ### todo/get/valid.js
119
+
120
+ This file validates the `get:id` method's input, ensuring that the provided `id` is a string and exists in the `todo` model:
121
+
122
+ ```js
123
+ // todo/get/valid.js
124
+ import { z, DB_VALIDATION } from 'minimonolith';
125
+
126
+ export default (MODELS, ROUTE_CODE) => ({
127
+ id: z.string().superRefine(
128
+ DB_VALIDATION.exists(MODELS.todo, 'id', { parseInt: true })
129
+ ),
130
+ })
131
+ ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minimonolith",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.12.1",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -10,7 +10,6 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "lambda-api": "^1.0.1",
13
- "mysql2": "^3.2.0",
14
13
  "sequelize": "^6.30.0",
15
14
  "zod": "^3.21.4"
16
15
  },
@@ -19,6 +18,7 @@
19
18
  "@aws-sdk/s3-request-presigner": "^3.304.0",
20
19
  "dotenv": "^16.0.3",
21
20
  "jest": "^29.5.0",
21
+ "mysql2": "^3.2.0",
22
22
  "sqlite3": "^5.1.6"
23
23
  }
24
24
  }
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
 
3
3
  const DB_VALIDATION = {
4
4
  exists: (model, field, options=undefined) => async (rawValue, ctx) => {
5
- const value = options?.parseInt ? parseInt(rawValue) : value;
5
+ const value = options?.parseInt ? parseInt(rawValue) : rawValue;
6
6
  const valueCount = await model.count({ where: { [field]: value } });
7
7
  if (valueCount == 0) {
8
8
  ctx.addIssue({
@@ -13,7 +13,7 @@ const DB_VALIDATION = {
13
13
  },
14
14
 
15
15
  notExists: (model, field, options=undefined) => async (rawValue, ctx) => {
16
- const value = options?.parseInt ? parseInt(rawValue) : value;
16
+ const value = options?.parseInt ? parseInt(rawValue) : rawValue;
17
17
  const valueCount = await model.count({ where: { [field]: value } });
18
18
  if (valueCount > 0) {
19
19
  ctx.addIssue({