mango-orm 1.0.0
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 +15 -0
- package/README.md +1244 -0
- package/dist/src/mango.d.ts +276 -0
- package/dist/src/mango.js +713 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,1244 @@
|
|
|
1
|
+
# ๐ฅญ Mango ORM
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**A lightweight, type-safe MySQL ORM for Node.js and TypeScript**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/mango-orm)
|
|
8
|
+
[](https://opensource.org/licenses/ISC)
|
|
9
|
+
[](https://www.typescriptlang.org/)
|
|
10
|
+
|
|
11
|
+
[Features](#-features) โข [Installation](#-installation) โข [Quick Start](#-quick-start) โข [Documentation](#-complete-documentation) โข [Examples](#-usage-examples)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## โ ๏ธ Development Status
|
|
18
|
+
|
|
19
|
+
**This project is currently under active development.** Features are being added and refined. The API may change in future versions. Not recommended for production use yet.
|
|
20
|
+
|
|
21
|
+
## ๐ Table of Contents
|
|
22
|
+
|
|
23
|
+
- [Features](#-features)
|
|
24
|
+
- [Installation](#-installation)
|
|
25
|
+
- [Quick Start](#-quick-start)
|
|
26
|
+
- [Complete Documentation](#-complete-documentation)
|
|
27
|
+
- [Database Connection](#1-database-connection)
|
|
28
|
+
- [Creating Tables](#2-creating-tables)
|
|
29
|
+
- [Inserting Data](#3-inserting-data)
|
|
30
|
+
- [Querying Data](#4-querying-data)
|
|
31
|
+
- [Filtering & Conditions](#5-filtering--conditions-where-and-or)
|
|
32
|
+
- [Updating Data](#6-updating-data)
|
|
33
|
+
- [Deleting Data](#7-deleting-data)
|
|
34
|
+
- [Advanced Features](#8-advanced-features)
|
|
35
|
+
- [API Reference](#-api-reference)
|
|
36
|
+
- [Security](#-security)
|
|
37
|
+
- [Contributing](#-contributing)
|
|
38
|
+
|
|
39
|
+
## โจ Features
|
|
40
|
+
|
|
41
|
+
- ๐ **Type-safe**: Full TypeScript support with generics for compile-time safety
|
|
42
|
+
- ๐ **Fluent API**: Chainable methods for readable, expressive queries
|
|
43
|
+
- ๐ก๏ธ **SQL Injection Protection**: Parameterized queries throughout for security
|
|
44
|
+
- ๐ **Connection Pooling**: Built-in MySQL connection pool for better performance
|
|
45
|
+
- ๐ฆ **Auto-Discovery**: Automatically loads existing table schemas on connect
|
|
46
|
+
- โก **Zero Config**: Works out of the box with sensible defaults
|
|
47
|
+
- ๐ฏ **Simple API**: Intuitive, easy to learn, and quick to master
|
|
48
|
+
- ๐ **Query Builder**: WHERE, AND, OR conditions with full operator support
|
|
49
|
+
- โป๏ธ **CRUD Complete**: Create, Read, Update, Delete operations all supported
|
|
50
|
+
- ๐งน **Clean Code**: Well-documented, formatted, and maintainable codebase
|
|
51
|
+
|
|
52
|
+
## ๐ฆ Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Using npm
|
|
56
|
+
npm install mango-orm mysql @types/mysql
|
|
57
|
+
|
|
58
|
+
# Using yarn
|
|
59
|
+
yarn add mango-orm mysql @types/mysql
|
|
60
|
+
|
|
61
|
+
# Using pnpm (recommended)
|
|
62
|
+
pnpm add mango-orm mysql @types/mysql
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Requirements
|
|
66
|
+
|
|
67
|
+
- **Node.js**: >= 14.x
|
|
68
|
+
- **TypeScript**: >= 4.5 (for TypeScript projects)
|
|
69
|
+
- **MySQL**: >= 5.7 or MariaDB >= 10.2
|
|
70
|
+
|
|
71
|
+
## ๐ Quick Start
|
|
72
|
+
|
|
73
|
+
Get started with Mango ORM in less than 5 minutes!
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { Mango } from "mango-orm";
|
|
77
|
+
|
|
78
|
+
// 1. Initialize and connect
|
|
79
|
+
const mango = new Mango();
|
|
80
|
+
await mango.connect({
|
|
81
|
+
host: "localhost",
|
|
82
|
+
user: "root",
|
|
83
|
+
password: "your_password",
|
|
84
|
+
database: "your_database"
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 2. Define your data structure
|
|
88
|
+
interface User {
|
|
89
|
+
id?: number;
|
|
90
|
+
username: string;
|
|
91
|
+
email: string;
|
|
92
|
+
age?: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 3. Create a table
|
|
96
|
+
const users = await mango.createTable<User>("users", {
|
|
97
|
+
id: mango.types().int().autoIncrement().primaryKey(),
|
|
98
|
+
username: mango.types().varchar(50).notNull().unique(),
|
|
99
|
+
email: mango.types().varchar(100).notNull(),
|
|
100
|
+
age: mango.types().int()
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// 4. Insert data
|
|
104
|
+
await users.insertOne({
|
|
105
|
+
username: "john_doe",
|
|
106
|
+
email: "john@example.com",
|
|
107
|
+
age: 25
|
|
108
|
+
}).execute();
|
|
109
|
+
|
|
110
|
+
// 5. Query data with conditions
|
|
111
|
+
const adults = await users
|
|
112
|
+
.selectAll()
|
|
113
|
+
.where("age", ">=", 18)
|
|
114
|
+
.orderBy("username")
|
|
115
|
+
.execute();
|
|
116
|
+
|
|
117
|
+
console.log(adults);
|
|
118
|
+
|
|
119
|
+
// 6. Update data
|
|
120
|
+
await users
|
|
121
|
+
.update({ age: 26 })
|
|
122
|
+
.where("username", "=", "john_doe")
|
|
123
|
+
.execute();
|
|
124
|
+
|
|
125
|
+
// 7. Delete data
|
|
126
|
+
await users
|
|
127
|
+
.delete()
|
|
128
|
+
.where("age", "<", 18)
|
|
129
|
+
.execute();
|
|
130
|
+
|
|
131
|
+
// 8. Disconnect when done
|
|
132
|
+
await mango.disconnect();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## ๐ Complete Documentation
|
|
138
|
+
|
|
139
|
+
### 1. Database Connection
|
|
140
|
+
|
|
141
|
+
#### Basic Connection
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const mango = new Mango();
|
|
145
|
+
|
|
146
|
+
await mango.connect({
|
|
147
|
+
host: "localhost", // MySQL server host
|
|
148
|
+
user: "root", // Database username
|
|
149
|
+
password: "mypassword", // Database password
|
|
150
|
+
database: "myapp" // Database name
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Connection Features
|
|
155
|
+
|
|
156
|
+
- โ
**Auto-connects** using connection pooling for performance
|
|
157
|
+
- โ
**Auto-discovers** all existing tables and their schemas
|
|
158
|
+
- โ
**Validates** connection parameters
|
|
159
|
+
- โ
**Returns** Mango instance for chaining
|
|
160
|
+
|
|
161
|
+
#### Disconnect
|
|
162
|
+
|
|
163
|
+
Always disconnect when your application is shutting down:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
await mango.disconnect();
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 2. Creating Tables
|
|
170
|
+
|
|
171
|
+
#### Define Your Schema
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
interface Post {
|
|
175
|
+
id?: number;
|
|
176
|
+
title: string;
|
|
177
|
+
content: string;
|
|
178
|
+
author_id: number;
|
|
179
|
+
created_at?: Date;
|
|
180
|
+
is_published?: boolean;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const posts = await mango.createTable<Post>("posts", {
|
|
184
|
+
id: mango.types().int().autoIncrement().primaryKey(),
|
|
185
|
+
title: mango.types().varchar(200).notNull(),
|
|
186
|
+
content: mango.types().text().notNull(),
|
|
187
|
+
author_id: mango.types().int().notNull(),
|
|
188
|
+
created_at: mango.types().timeStamp(),
|
|
189
|
+
is_published: mango.types().boolean()
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Available Data Types
|
|
194
|
+
|
|
195
|
+
| Type | Method | Example | SQL Output |
|
|
196
|
+
|------|--------|---------|------------|
|
|
197
|
+
| Integer | `int()` | `types().int()` | `INT` |
|
|
198
|
+
| Big Integer | `bigInt()` | `types().bigInt()` | `BIGINT` |
|
|
199
|
+
| Float | `float()` | `types().float()` | `FLOAT` |
|
|
200
|
+
| Variable String | `varchar(n)` | `types().varchar(255)` | `VARCHAR(255)` |
|
|
201
|
+
| Fixed String | `char(n)` | `types().char(10)` | `CHAR(10)` |
|
|
202
|
+
| Text | `text()` | `types().text()` | `TEXT` |
|
|
203
|
+
| Date | `date()` | `types().date()` | `DATE` |
|
|
204
|
+
| DateTime | `dateTime()` | `types().dateTime()` | `DATETIME` |
|
|
205
|
+
| Timestamp | `timeStamp()` | `types().timeStamp()` | `TIMESTAMP` |
|
|
206
|
+
| Boolean | `boolean()` | `types().boolean()` | `BOOLEAN` |
|
|
207
|
+
| Tiny Integer | `tinyInt(n)` | `types().tinyInt(1)` | `TINYINT(1)` |
|
|
208
|
+
|
|
209
|
+
#### Type Modifiers
|
|
210
|
+
|
|
211
|
+
Chain modifiers to customize your columns:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
mango.types()
|
|
215
|
+
.int() // Column type
|
|
216
|
+
.notNull() // Cannot be NULL
|
|
217
|
+
.autoIncrement() // Auto-incrementing
|
|
218
|
+
.primaryKey() // Primary key
|
|
219
|
+
.unique() // Unique constraint
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Common Patterns:**
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Primary key (typical ID column)
|
|
226
|
+
id: mango.types().int().autoIncrement().primaryKey()
|
|
227
|
+
|
|
228
|
+
// Required email with uniqueness
|
|
229
|
+
email: mango.types().varchar(100).notNull().unique()
|
|
230
|
+
|
|
231
|
+
// Optional text field
|
|
232
|
+
bio: mango.types().text()
|
|
233
|
+
|
|
234
|
+
// Required foreign key
|
|
235
|
+
user_id: mango.types().int().notNull()
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Accessing Existing Tables
|
|
239
|
+
|
|
240
|
+
After connection, all existing tables are automatically loaded:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// Get a typed table instance
|
|
244
|
+
const existingUsers = mango.selectTable<User>("users");
|
|
245
|
+
|
|
246
|
+
// Query immediately
|
|
247
|
+
const data = await existingUsers.selectAll().execute();
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### Table Information
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const tableName = users.getName(); // Returns: "users"
|
|
256
|
+
const fields = users.getFields(); // Returns: ["id", "username", "email"]
|
|
257
|
+
|
|
258
|
+
console.log(`Table: ${tableName}`);
|
|
259
|
+
console.log(`Columns:`, fields);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### 5. Filtering & Conditions (WHERE, AND, OR)
|
|
265
|
+
|
|
266
|
+
#### WHERE Clause
|
|
267
|
+
|
|
268
|
+
Filter results with WHERE conditions:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// Find user by username
|
|
272
|
+
const user = await users
|
|
273
|
+
.selectAll()
|
|
274
|
+
.where("username", "=", "john_doe")
|
|
275
|
+
.execute();
|
|
276
|
+
|
|
277
|
+
// Find adult users
|
|
278
|
+
const adults = await users
|
|
279
|
+
.selectAll()
|
|
280
|
+
.where("age", ">=", 18)
|
|
281
|
+
.execute();
|
|
282
|
+
|
|
283
|
+
// Find published posts
|
|
284
|
+
const published = await posts
|
|
285
|
+
.selectAll()
|
|
286
|
+
.where("is_published", "=", true)
|
|
287
|
+
.execute();
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### Supported Operators
|
|
291
|
+
|
|
292
|
+
| Operator | Description | Example |
|
|
293
|
+
|----------|-------------|---------|
|
|
294
|
+
| `=` | Equal to | `where("age", "=", 25)` |
|
|
295
|
+
| `!=` or `<>` | Not equal | `where("status", "!=", "deleted")` |
|
|
296
|
+
| `>` | Greater than | `where("age", ">", 18)` |
|
|
297
|
+
| `<` | Less than | `where("price", "<", 100)` |
|
|
298
|
+
| `>=` | Greater or equal | `where("age", ">=", 21)` |
|
|
299
|
+
| `<=` | Less or equal | `where("stock", "<=", 10)` |
|
|
300
|
+
| `LIKE` | Pattern match | `where("email", "LIKE", "%@gmail.com")` |
|
|
301
|
+
| `NOT LIKE` | Not matching | `where("name", "NOT LIKE", "test%")` |
|
|
302
|
+
| `IN` | In list | Use `whereIn()` method |
|
|
303
|
+
| `NOT IN` | Not in list | Use `whereNotIn()` method |
|
|
304
|
+
|
|
305
|
+
#### AND Conditions
|
|
306
|
+
|
|
307
|
+
Combine multiple conditions (all must be true):
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// Find young adults
|
|
311
|
+
const youngAdults = await users
|
|
312
|
+
.selectAll()
|
|
313
|
+
.where("age", ">=", 18)
|
|
314
|
+
.and("age", "<=", 30)
|
|
315
|
+
.execute();
|
|
316
|
+
|
|
317
|
+
// Find published posts by specific author
|
|
318
|
+
const authorPosts = await posts
|
|
319
|
+
.selectAll()
|
|
320
|
+
.where("author_id", "=", 1)
|
|
321
|
+
.and("is_published", "=", true)
|
|
322
|
+
.execute();
|
|
323
|
+
|
|
324
|
+
// Multiple AND conditions
|
|
325
|
+
const specificUsers = await users
|
|
326
|
+
.selectAll()
|
|
327
|
+
.where("age", ">", 25)
|
|
328
|
+
.and("age", "<", 40)
|
|
329
|
+
.and("email", "LIKE", "%@company.com")
|
|
330
|
+
.execute();
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### OR Conditions
|
|
334
|
+
|
|
335
|
+
Alternative conditions (any can be true):
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// Find users who are either admins or moderators
|
|
339
|
+
const staff = await users
|
|
340
|
+
.selectAll()
|
|
341
|
+
.where("role", "=", "admin")
|
|
342
|
+
.or("role", "=", "moderator")
|
|
343
|
+
.execute();
|
|
344
|
+
|
|
345
|
+
// Find urgent or high priority tasks
|
|
346
|
+
const important = await tasks
|
|
347
|
+
.selectAll()
|
|
348
|
+
.where("priority", "=", "urgent")
|
|
349
|
+
.or("priority", "=", "high")
|
|
350
|
+
.execute();
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Combining AND/OR
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
// Complex conditions: (age >= 18 AND age <= 65) OR is_verified = true
|
|
357
|
+
const eligible = await users
|
|
358
|
+
.selectAll()
|
|
359
|
+
.where("age", ">=", 18)
|
|
360
|
+
.and("age", "<=", 65)
|
|
361
|
+
.or("is_verified", "=", true)
|
|
362
|
+
.execute();
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### WHERE IN / NOT IN
|
|
366
|
+
|
|
367
|
+
Check if value exists in a list:
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// Find users with specific IDs
|
|
371
|
+
const selectedUsers = await users
|
|
372
|
+
.selectAll()
|
|
373
|
+
.whereIn("id", [1, 5, 10, 15])
|
|
374
|
+
.execute();
|
|
375
|
+
|
|
376
|
+
// Find posts by multiple authors
|
|
377
|
+
const multiAuthorPosts = await posts
|
|
378
|
+
.selectAll()
|
|
379
|
+
.whereIn("author_id", [1, 2, 3])
|
|
380
|
+
.execute();
|
|
381
|
+
|
|
382
|
+
// Exclude specific users
|
|
383
|
+
const otherUsers = await users
|
|
384
|
+
.selectAll()
|
|
385
|
+
.whereNotIn("id", [1, 2, 3])
|
|
386
|
+
.execute();
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Working with Falsy Values
|
|
390
|
+
|
|
391
|
+
**Important:** WHERE conditions support falsy values (0, false, empty string):
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// โ
Works correctly with 0
|
|
395
|
+
const inactiveUsers = await users
|
|
396
|
+
.selectAll()
|
|
397
|
+
.where("login_count", "=", 0)
|
|
398
|
+
.execute();
|
|
399
|
+
|
|
400
|
+
// โ
Works correctly with false
|
|
401
|
+
const unpublished = await posts
|
|
402
|
+
.selectAll()
|
|
403
|
+
.where("is_published", "=", false)
|
|
404
|
+
.execute();
|
|
405
|
+
|
|
406
|
+
// โ
Works correctly with empty string
|
|
407
|
+
const noDescription = await products
|
|
408
|
+
.selectAll()
|
|
409
|
+
.where("description", "=", "")
|
|
410
|
+
.execute();
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
### 6. Updating Data
|
|
416
|
+
|
|
417
|
+
Update existing records with WHERE conditions:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
// Update single field
|
|
421
|
+
await users
|
|
422
|
+
.update({ age: 26 })
|
|
423
|
+
.where("username", "=", "john_doe")
|
|
424
|
+
.execute();
|
|
425
|
+
|
|
426
|
+
// Update multiple fields
|
|
427
|
+
await users
|
|
428
|
+
.update({
|
|
429
|
+
email: "newemail@example.com",
|
|
430
|
+
age: 30,
|
|
431
|
+
is_verified: true
|
|
432
|
+
})
|
|
433
|
+
.where("id", "=", 5)
|
|
434
|
+
.execute();
|
|
435
|
+
|
|
436
|
+
// Update with AND conditions
|
|
437
|
+
await posts
|
|
438
|
+
.update({ is_published: true })
|
|
439
|
+
.where("author_id", "=", 1)
|
|
440
|
+
.and("title", "LIKE", "%Draft%")
|
|
441
|
+
.execute();
|
|
442
|
+
|
|
443
|
+
// Bulk update
|
|
444
|
+
await products
|
|
445
|
+
.update({ stock: 0 })
|
|
446
|
+
.where("stock", "<", 5)
|
|
447
|
+
.execute();
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Important Notes:**
|
|
451
|
+
- โ
Always use WHERE to avoid updating all rows
|
|
452
|
+
- โ
Validates all field names exist in table
|
|
453
|
+
- โ
Supports all data types
|
|
454
|
+
- โ
Uses prepared statements for security
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
### 7. Deleting Data
|
|
459
|
+
|
|
460
|
+
Remove records from table:
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
// Delete specific user
|
|
464
|
+
await users
|
|
465
|
+
.delete()
|
|
466
|
+
.where("id", "=", 10)
|
|
467
|
+
.execute();
|
|
468
|
+
|
|
469
|
+
// Delete with conditions
|
|
470
|
+
await posts
|
|
471
|
+
.delete()
|
|
472
|
+
.where("is_published", "=", false)
|
|
473
|
+
.and("created_at", "<", "2024-01-01")
|
|
474
|
+
.execute();
|
|
475
|
+
|
|
476
|
+
// Delete using IN
|
|
477
|
+
await users
|
|
478
|
+
.delete()
|
|
479
|
+
.whereIn("id", [5, 10, 15])
|
|
480
|
+
.execute();
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**โ ๏ธ Warning:** DELETE without WHERE will remove ALL rows!
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// This deletes everything! Use with caution
|
|
487
|
+
await users.delete().execute(); // โ Dangerous!
|
|
488
|
+
|
|
489
|
+
// Better: Use truncate for clearing table
|
|
490
|
+
await users.truncate().execute(); // โ
Explicit intent
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
### 8. Advanced Features
|
|
496
|
+
|
|
497
|
+
#### Join Tables
|
|
498
|
+
|
|
499
|
+
Combine data from multiple tables:
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
const userPosts = await users
|
|
503
|
+
.selectAll()
|
|
504
|
+
.join("INNER", "posts", {
|
|
505
|
+
left: "users.id",
|
|
506
|
+
operator: "=",
|
|
507
|
+
right: "posts.author_id"
|
|
508
|
+
})
|
|
509
|
+
.execute();
|
|
510
|
+
|
|
511
|
+
// LEFT JOIN
|
|
512
|
+
const allUsersWithPosts = await users
|
|
513
|
+
.selectAll()
|
|
514
|
+
.join("LEFT", "posts", {
|
|
515
|
+
left: "users.id",
|
|
516
|
+
operator: "=",
|
|
517
|
+
right: "posts.author_id"
|
|
518
|
+
})
|
|
519
|
+
.execute();
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Join Types:** `INNER`, `LEFT`, `RIGHT`, `FULL`
|
|
523
|
+
|
|
524
|
+
#### Modifying Table Schema
|
|
525
|
+
|
|
526
|
+
**Add New Columns:**
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
await users.addColumns({
|
|
530
|
+
phone: mango.types().varchar(20),
|
|
531
|
+
address: mango.types().text(),
|
|
532
|
+
verified_at: mango.types().dateTime()
|
|
533
|
+
}).execute();
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Remove Columns:**
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
await users.removeColumns(["phone", "address"]).execute();
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
#### Truncate Table
|
|
543
|
+
|
|
544
|
+
Remove all rows (faster than DELETE):
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
await users.truncate().execute();
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
#### Drop Table
|
|
551
|
+
|
|
552
|
+
Permanently delete a table:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
await mango.dropTable("old_table");
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
#### Custom Raw Queries
|
|
559
|
+
|
|
560
|
+
For complex queries not covered by the query builder:
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
// Custom SELECT
|
|
564
|
+
const result = await users.customQuery<User>(
|
|
565
|
+
"SELECT * FROM users WHERE age > ? AND email LIKE ?",
|
|
566
|
+
[18, "%@gmail.com"]
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
// Custom INSERT with RETURNING (if supported)
|
|
570
|
+
const inserted = await posts.customQuery(
|
|
571
|
+
"INSERT INTO posts (title, content) VALUES (?, ?) RETURNING id",
|
|
572
|
+
["New Post", "Content here"]
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
// Complex JOIN
|
|
576
|
+
const complexQuery = await users.customQuery(
|
|
577
|
+
`SELECT u.*, COUNT(p.id) as post_count
|
|
578
|
+
FROM users u
|
|
579
|
+
LEFT JOIN posts p ON u.id = p.author_id
|
|
580
|
+
GROUP BY u.id
|
|
581
|
+
HAVING post_count > ?`,
|
|
582
|
+
[5]
|
|
583
|
+
);
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Tips:**
|
|
587
|
+
- Always use `?` placeholders for values
|
|
588
|
+
- Never concatenate user input into queries
|
|
589
|
+
- Use for complex operations like GROUP BY, HAVING, subqueries
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
### 3. Inserting Data
|
|
594
|
+
|
|
595
|
+
#### Single Row Insert
|
|
596
|
+
|
|
597
|
+
Insert one record at a time:
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
// Basic insert
|
|
601
|
+
await users.insertOne({
|
|
602
|
+
username: "jane_doe",
|
|
603
|
+
email: "jane@example.com",
|
|
604
|
+
age: 28
|
|
605
|
+
}).execute();
|
|
606
|
+
|
|
607
|
+
// Insert with all fields
|
|
608
|
+
await posts.insertOne({
|
|
609
|
+
title: "Getting Started with Mango",
|
|
610
|
+
content: "This is a comprehensive guide...",
|
|
611
|
+
author_id: 1,
|
|
612
|
+
is_published: true
|
|
613
|
+
}).execute();
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Features:**
|
|
617
|
+
- โ
Type-safe: TypeScript validates field names and types
|
|
618
|
+
- โ
SQL Injection protected with prepared statements
|
|
619
|
+
- โ
Auto-validates fields exist in table schema
|
|
620
|
+
- โ
Supports all data types (numbers, strings, booleans, dates, null)
|
|
621
|
+
|
|
622
|
+
#### Bulk Insert
|
|
623
|
+
|
|
624
|
+
Insert multiple rows efficiently:
|
|
625
|
+
|
|
626
|
+
```typescript
|
|
627
|
+
await posts.insertMany(
|
|
628
|
+
["title", "content", "author_id"], // Column names
|
|
629
|
+
[ // Data rows
|
|
630
|
+
["First Post", "Content here", 1],
|
|
631
|
+
["Second Post", "More content", 1],
|
|
632
|
+
["Third Post", "Even more", 2]
|
|
633
|
+
]
|
|
634
|
+
).execute();
|
|
635
|
+
|
|
636
|
+
// Insert 1000 records efficiently
|
|
637
|
+
const bulkData = [];
|
|
638
|
+
for (let i = 0; i < 1000; i++) {
|
|
639
|
+
bulkData.push([`User ${i}`, `user${i}@example.com`, 20 + i % 50]);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
await users.insertMany(
|
|
643
|
+
["username", "email", "age"],
|
|
644
|
+
bulkData
|
|
645
|
+
).execute();
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
**Performance:**
|
|
649
|
+
- โ
Single query for all rows (much faster than multiple inserts)
|
|
650
|
+
- โ
Efficient for batch operations
|
|
651
|
+
- โ
Validates all rows have same number of columns
|
|
652
|
+
|
|
653
|
+
### 4. Querying Data
|
|
654
|
+
|
|
655
|
+
#### Select All Columns
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
// Get all users
|
|
659
|
+
const allUsers = await users.selectAll().execute();
|
|
660
|
+
|
|
661
|
+
// Get all posts
|
|
662
|
+
const allPosts = await posts.selectAll().execute();
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
#### Select Specific Columns
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
// Select only username and email
|
|
669
|
+
const userContacts = await users
|
|
670
|
+
.selectColumns(["username", "email"])
|
|
671
|
+
.execute();
|
|
672
|
+
|
|
673
|
+
// Select specific post fields
|
|
674
|
+
const postTitles = await posts
|
|
675
|
+
.selectColumns(["id", "title", "created_at"])
|
|
676
|
+
.execute();
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
#### Select Distinct Values
|
|
680
|
+
|
|
681
|
+
Get unique values only:
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
// Get unique email domains
|
|
685
|
+
const uniqueEmails = await users
|
|
686
|
+
.selectDistinctColumns(["email"])
|
|
687
|
+
.execute();
|
|
688
|
+
|
|
689
|
+
// Get unique authors
|
|
690
|
+
const uniqueAuthors = await posts
|
|
691
|
+
.selectDistinctColumns(["author_id"])
|
|
692
|
+
.execute();
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
#### Ordering Results
|
|
696
|
+
|
|
697
|
+
Sort your query results:
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
// Order by username ascending (A-Z)
|
|
701
|
+
const sortedUsers = await users
|
|
702
|
+
.selectAll()
|
|
703
|
+
.orderBy("username")
|
|
704
|
+
.sort(1) // 1 = ASC, -1 = DESC
|
|
705
|
+
.execute();
|
|
706
|
+
|
|
707
|
+
// Order by age descending (highest first)
|
|
708
|
+
const oldestFirst = await users
|
|
709
|
+
.selectAll()
|
|
710
|
+
.orderBy("age")
|
|
711
|
+
.sort(-1) // Descending order
|
|
712
|
+
.execute();
|
|
713
|
+
|
|
714
|
+
// Order posts by creation date (newest first)
|
|
715
|
+
const recentPosts = await posts
|
|
716
|
+
.selectAll()
|
|
717
|
+
.orderBy("created_at")
|
|
718
|
+
.sort(-1)
|
|
719
|
+
.execute();
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
#### Limiting and Pagination
|
|
723
|
+
|
|
724
|
+
```typescript
|
|
725
|
+
// Get first 10 users
|
|
726
|
+
const firstTen = await users
|
|
727
|
+
.selectAll()
|
|
728
|
+
.limit(10)
|
|
729
|
+
.execute();
|
|
730
|
+
|
|
731
|
+
// Get 10 users, skip first 20 (page 3, 10 per page)
|
|
732
|
+
const page3 = await users
|
|
733
|
+
.selectAll()
|
|
734
|
+
.limit(10)
|
|
735
|
+
.offset(20)
|
|
736
|
+
.execute();
|
|
737
|
+
|
|
738
|
+
// Complete pagination example
|
|
739
|
+
const PAGE_SIZE = 25;
|
|
740
|
+
const page = 2; // Get page 2
|
|
741
|
+
|
|
742
|
+
const paginatedUsers = await users
|
|
743
|
+
.selectAll()
|
|
744
|
+
.orderBy("id")
|
|
745
|
+
.sort(1)
|
|
746
|
+
.limit(PAGE_SIZE)
|
|
747
|
+
.offset((page - 1) * PAGE_SIZE)
|
|
748
|
+
.execute();
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### Modifying Tables
|
|
752
|
+
|
|
753
|
+
**Add Columns:**
|
|
754
|
+
```typescript
|
|
755
|
+
await users.addColumns({
|
|
756
|
+
age: mango.types().int(),
|
|
757
|
+
phone: mango.types().varchar(20)
|
|
758
|
+
}).execute();
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
**Remove Columns:**
|
|
762
|
+
```typescript
|
|
763
|
+
await users.removeColumns(["age"]).execute();
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
### Truncate and Drop
|
|
767
|
+
|
|
768
|
+
**Truncate Table:**
|
|
769
|
+
```typescript
|
|
770
|
+
await users.truncate().execute();
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Drop Table:**
|
|
774
|
+
```typescript
|
|
775
|
+
await mango.dropTable("users");
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### Custom Queries
|
|
779
|
+
|
|
780
|
+
```typescript
|
|
781
|
+
const result = await users.customQuery<User>(
|
|
782
|
+
"SELECT * FROM users WHERE age > ?",
|
|
783
|
+
[18]
|
|
784
|
+
);
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## ๐ง API Reference
|
|
790
|
+
|
|
791
|
+
### Mango Class
|
|
792
|
+
|
|
793
|
+
Main class for database connection and management.
|
|
794
|
+
|
|
795
|
+
| Method | Parameters | Returns | Description |
|
|
796
|
+
|--------|------------|---------|-------------|
|
|
797
|
+
| `connect(config)` | `{host, user, password, database}` | `Promise<Mango>` | Connect to MySQL and load tables |
|
|
798
|
+
| `disconnect()` | - | `Promise<void>` | Close connection pool |
|
|
799
|
+
| `createTable<T>(name, fields)` | `name: string`, `fields: Record` | `Promise<MangoTable<T>>` | Create new table |
|
|
800
|
+
| `selectTable<T>(name)` | `name: string` | `MangoTable<T>` | Get existing table instance |
|
|
801
|
+
| `dropTable(name)` | `name: string` | `Promise<void>` | Drop table permanently |
|
|
802
|
+
| `getTables()` | - | `MangoTable[]` | Get all table instances |
|
|
803
|
+
| `types()` | - | `MangoType` | Get schema type builder |
|
|
804
|
+
| `customQuery<T>(query, params)` | `query: string`, `params: any[]` | `Promise<T>` | Execute raw SQL |
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
### MangoTable<T> Class
|
|
809
|
+
|
|
810
|
+
Query builder for table operations (all methods chainable except `execute()`).
|
|
811
|
+
|
|
812
|
+
#### Query Building Methods
|
|
813
|
+
|
|
814
|
+
| Method | Parameters | Returns | Description |
|
|
815
|
+
|--------|------------|---------|-------------|
|
|
816
|
+
| `selectAll()` | - | `this` | Select all columns |
|
|
817
|
+
| `selectColumns(cols)` | `cols: string[]` | `this` | Select specific columns |
|
|
818
|
+
| `selectDistinctColumns(cols)` | `cols: string[]` | `this` | Select unique values |
|
|
819
|
+
| `orderBy(column)` | `column: string` | `this` | Order results by column |
|
|
820
|
+
| `sort(direction)` | `1` (ASC) or `-1` (DESC) | `this` | Sort direction |
|
|
821
|
+
| `limit(n)` | `n: number` | `this` | Limit number of results |
|
|
822
|
+
| `offset(n)` | `n: number` | `this` | Skip n rows |
|
|
823
|
+
|
|
824
|
+
#### Filtering Methods
|
|
825
|
+
|
|
826
|
+
| Method | Parameters | Returns | Description |
|
|
827
|
+
|--------|------------|---------|-------------|
|
|
828
|
+
| `where(field, op, value)` | `field: string`, `op: string`, `value: any` | `this` | Add WHERE condition |
|
|
829
|
+
| `and(field, op, value)` | `field: string`, `op: string`, `value: any` | `this` | Add AND condition |
|
|
830
|
+
| `or(field, op, value)` | `field: string`, `op: string`, `value: any` | `this` | Add OR condition |
|
|
831
|
+
| `whereIn(field, values)` | `field: string`, `values: any[]` | `this` | WHERE field IN (values) |
|
|
832
|
+
| `whereNotIn(field, values)` | `field: string`, `values: any[]` | `this` | WHERE field NOT IN (values) |
|
|
833
|
+
|
|
834
|
+
#### Data Modification Methods
|
|
835
|
+
|
|
836
|
+
| Method | Parameters | Returns | Description |
|
|
837
|
+
|--------|------------|---------|-------------|
|
|
838
|
+
| `insertOne(data)` | `data: Record<string, any>` | `this` | Insert single row |
|
|
839
|
+
| `insertMany(fields, data)` | `fields: string[]`, `data: any[][]` | `this` | Insert multiple rows |
|
|
840
|
+
| `update(data)` | `data: Record<string, any>` | `this` | Update rows (use with WHERE) |
|
|
841
|
+
| `delete()` | - | `this` | Delete rows (use with WHERE) |
|
|
842
|
+
|
|
843
|
+
#### Schema Modification Methods
|
|
844
|
+
|
|
845
|
+
| Method | Parameters | Returns | Description |
|
|
846
|
+
|--------|------------|---------|-------------|
|
|
847
|
+
| `addColumns(fields)` | `fields: Record<string, MangoType>` | `this` | Add new columns |
|
|
848
|
+
| `removeColumns(fields)` | `fields: string[]` | `this` | Remove columns |
|
|
849
|
+
| `truncate()` | - | `this` | Remove all rows |
|
|
850
|
+
|
|
851
|
+
#### Utility Methods
|
|
852
|
+
|
|
853
|
+
| Method | Parameters | Returns | Description |
|
|
854
|
+
|--------|------------|---------|-------------|
|
|
855
|
+
| `execute()` | - | `Promise<T[]>` | Execute the built query |
|
|
856
|
+
| `customQuery<Type>(query, params)` | `query: string`, `params: any[]` | `Promise<Type[]>` | Execute custom SQL |
|
|
857
|
+
| `getName()` | - | `string` | Get table name |
|
|
858
|
+
| `getFields()` | - | `string[]` | Get column names (copy) |
|
|
859
|
+
| `getQuery()` | - | `MangoQuery` | Get query object (advanced) |
|
|
860
|
+
|
|
861
|
+
#### Advanced Methods
|
|
862
|
+
|
|
863
|
+
| Method | Parameters | Returns | Description |
|
|
864
|
+
|--------|------------|---------|-------------|
|
|
865
|
+
| `join(type, table, condition)` | `type`, `table: string`, `condition` | `this` | Join tables |
|
|
866
|
+
| `commit()` | - | `this` | Add COMMIT to query |
|
|
867
|
+
| `rollback()` | - | `this` | Add ROLLBACK to query |
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
### MangoType Class
|
|
872
|
+
|
|
873
|
+
Schema type builder for column definitions.
|
|
874
|
+
|
|
875
|
+
#### Data Types
|
|
876
|
+
|
|
877
|
+
| Method | Returns | SQL Output | Description |
|
|
878
|
+
|--------|---------|------------|-------------|
|
|
879
|
+
| `int()` | `this` | `INT` | Integer number |
|
|
880
|
+
| `bigInt()` | `this` | `BIGINT` | Large integer |
|
|
881
|
+
| `float()` | `this` | `FLOAT` | Floating point |
|
|
882
|
+
| `varchar(n)` | `this` | `VARCHAR(n)` | Variable string |
|
|
883
|
+
| `char(n)` | `this` | `CHAR(n)` | Fixed string |
|
|
884
|
+
| `text()` | `this` | `TEXT` | Long text |
|
|
885
|
+
| `date()` | `this` | `DATE` | Date only |
|
|
886
|
+
| `dateTime()` | `this` | `DATETIME` | Date and time |
|
|
887
|
+
| `timeStamp()` | `this` | `TIMESTAMP` | Auto-updating timestamp |
|
|
888
|
+
| `boolean()` | `this` | `BOOLEAN` | True/false |
|
|
889
|
+
| `tinyInt(n)` | `this` | `TINYINT(n)` | Small integer |
|
|
890
|
+
|
|
891
|
+
#### Modifiers
|
|
892
|
+
|
|
893
|
+
| Method | Returns | SQL Output | Description |
|
|
894
|
+
|--------|---------|------------|-------------|
|
|
895
|
+
| `notNull()` | `this` | `NOT NULL` | Cannot be null |
|
|
896
|
+
| `autoIncrement()` | `this` | `AUTO_INCREMENT` | Auto-incrementing |
|
|
897
|
+
| `primaryKey()` | `this` | `PRIMARY KEY` | Primary key |
|
|
898
|
+
| `unique()` | `this` | `UNIQUE` | Must be unique |
|
|
899
|
+
| `getQuery()` | `string` | - | Get built SQL string |
|
|
900
|
+
|
|
901
|
+
---
|
|
902
|
+
|
|
903
|
+
## ๐ Security
|
|
904
|
+
|
|
905
|
+
Mango ORM is built with security in mind:
|
|
906
|
+
|
|
907
|
+
### โ
Prepared Statements
|
|
908
|
+
|
|
909
|
+
All queries use parameterized statements to prevent SQL injection:
|
|
910
|
+
|
|
911
|
+
```typescript
|
|
912
|
+
// โ
SAFE - Values are parameterized
|
|
913
|
+
await users.insertOne({
|
|
914
|
+
username: userInput, // Automatically escaped
|
|
915
|
+
email: emailInput
|
|
916
|
+
}).execute();
|
|
917
|
+
|
|
918
|
+
// โ
SAFE - WHERE values are parameterized
|
|
919
|
+
await users
|
|
920
|
+
.selectAll()
|
|
921
|
+
.where("username", "=", userInput) // Safe from injection
|
|
922
|
+
.execute();
|
|
923
|
+
|
|
924
|
+
// โ
SAFE - Bulk insert with parameterized values
|
|
925
|
+
await posts.insertMany(
|
|
926
|
+
["title", "content"],
|
|
927
|
+
[[userTitle, userContent]] // All values escaped
|
|
928
|
+
).execute();
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### โ ๏ธ Custom Queries
|
|
932
|
+
|
|
933
|
+
When using `customQuery()`, **always use placeholders**:
|
|
934
|
+
|
|
935
|
+
```typescript
|
|
936
|
+
// โ
SAFE - Using placeholders
|
|
937
|
+
const results = await users.customQuery(
|
|
938
|
+
"SELECT * FROM users WHERE email = ?",
|
|
939
|
+
[userEmail]
|
|
940
|
+
);
|
|
941
|
+
|
|
942
|
+
// โ UNSAFE - String concatenation
|
|
943
|
+
const unsafe = await users.customQuery(
|
|
944
|
+
`SELECT * FROM users WHERE email = '${userEmail}'`, // SQL INJECTION RISK!
|
|
945
|
+
[]
|
|
946
|
+
);
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
### ๐ก๏ธ Field Validation
|
|
950
|
+
|
|
951
|
+
All operations validate field names against table schema:
|
|
952
|
+
|
|
953
|
+
```typescript
|
|
954
|
+
// โ
Validated - Throws error if 'invalid_field' doesn't exist
|
|
955
|
+
await users.insertOne({
|
|
956
|
+
invalid_field: "value" // Error: Field doesn't exist
|
|
957
|
+
}).execute();
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
### ๐ Connection Security
|
|
961
|
+
|
|
962
|
+
```typescript
|
|
963
|
+
// Use environment variables for credentials
|
|
964
|
+
await mango.connect({
|
|
965
|
+
host: process.env.DB_HOST,
|
|
966
|
+
user: process.env.DB_USER,
|
|
967
|
+
password: process.env.DB_PASSWORD,
|
|
968
|
+
database: process.env.DB_NAME
|
|
969
|
+
});
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
**Best Practices:**
|
|
973
|
+
- โ
Never commit credentials to source control
|
|
974
|
+
- โ
Use `.env` files with proper `.gitignore`
|
|
975
|
+
- โ
Rotate credentials regularly
|
|
976
|
+
- โ
Use least-privilege database users
|
|
977
|
+
- โ
Enable SSL/TLS for production databases
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
## ๐ง Current Limitations
|
|
982
|
+
|
|
983
|
+
While Mango is functional, some features are still in development:
|
|
984
|
+
|
|
985
|
+
- โณ **No Transaction Support**: BEGIN/COMMIT/ROLLBACK not fully implemented
|
|
986
|
+
- โณ **Limited JOIN Support**: Basic joins work but complex joins need testing
|
|
987
|
+
- โณ **No Migration System**: Schema versioning and migrations coming soon
|
|
988
|
+
- โณ **No Relations**: ORM-style relations (hasMany, belongsTo) not yet available
|
|
989
|
+
- โณ **No Query Caching**: Results are not cached (may add in future)
|
|
990
|
+
- โณ **No Connection Retry**: Failed connections don't auto-retry
|
|
991
|
+
- โณ **Single Database Only**: Cannot connect to multiple databases simultaneously
|
|
992
|
+
|
|
993
|
+
---
|
|
994
|
+
|
|
995
|
+
## ๐บ๏ธ Roadmap
|
|
996
|
+
|
|
997
|
+
### Short Term (v0.3.0)
|
|
998
|
+
- [x] WHERE clause support
|
|
999
|
+
- [x] UPDATE operations
|
|
1000
|
+
- [x] DELETE operations
|
|
1001
|
+
- [x] Comprehensive documentation
|
|
1002
|
+
- [ ] Transaction support (BEGIN, COMMIT, ROLLBACK)
|
|
1003
|
+
- [ ] Better error messages with stack traces
|
|
1004
|
+
- [ ] Connection pool configuration options
|
|
1005
|
+
|
|
1006
|
+
### Medium Term (v0.4.0)
|
|
1007
|
+
- [ ] Migration system
|
|
1008
|
+
- [ ] Seed data functionality
|
|
1009
|
+
- [ ] Query result caching
|
|
1010
|
+
- [ ] Batch operation optimization
|
|
1011
|
+
- [ ] Advanced JOIN support
|
|
1012
|
+
- [ ] Aggregate functions (COUNT, SUM, AVG, etc.)
|
|
1013
|
+
- [ ] GROUP BY and HAVING clauses
|
|
1014
|
+
|
|
1015
|
+
### Long Term (v1.0.0)
|
|
1016
|
+
- [ ] Relation support (hasOne, hasMany, belongsTo, manyToMany)
|
|
1017
|
+
- [ ] Eager loading and lazy loading
|
|
1018
|
+
- [ ] Model hooks (beforeCreate, afterUpdate, etc.)
|
|
1019
|
+
- [ ] Query event listeners
|
|
1020
|
+
- [ ] Schema validation
|
|
1021
|
+
- [ ] Multiple database connections
|
|
1022
|
+
- [ ] Read/write splitting
|
|
1023
|
+
- [ ] Query builder visual tool
|
|
1024
|
+
|
|
1025
|
+
---
|
|
1026
|
+
|
|
1027
|
+
## ๐ Example Project
|
|
1028
|
+
|
|
1029
|
+
### Quick Test
|
|
1030
|
+
|
|
1031
|
+
Check out the `/test` directory for complete working examples:
|
|
1032
|
+
|
|
1033
|
+
```bash
|
|
1034
|
+
# Clone the repository
|
|
1035
|
+
git clone https://github.com/devSiddharthKarn/Mango.git
|
|
1036
|
+
cd Mango
|
|
1037
|
+
|
|
1038
|
+
# Install dependencies
|
|
1039
|
+
pnpm install
|
|
1040
|
+
# or: npm install
|
|
1041
|
+
|
|
1042
|
+
# Configure database
|
|
1043
|
+
# Edit test/test.ts with your database credentials
|
|
1044
|
+
|
|
1045
|
+
# Run tests
|
|
1046
|
+
pnpm run test
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
### Example Code Structure
|
|
1050
|
+
|
|
1051
|
+
```
|
|
1052
|
+
Mango/
|
|
1053
|
+
โโโ src/
|
|
1054
|
+
โ โโโ mango.ts # Main ORM code
|
|
1055
|
+
โโโ test/
|
|
1056
|
+
โ โโโ test.ts # Basic usage examples
|
|
1057
|
+
โ โโโ test-operations.ts # Advanced operations
|
|
1058
|
+
โโโ package.json
|
|
1059
|
+
โโโ tsconfig.json
|
|
1060
|
+
โโโ README.md
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
### Sample Application
|
|
1064
|
+
|
|
1065
|
+
Here's a complete example application:
|
|
1066
|
+
|
|
1067
|
+
```typescript
|
|
1068
|
+
import { Mango } from "mango-orm";
|
|
1069
|
+
|
|
1070
|
+
interface User {
|
|
1071
|
+
id?: number;
|
|
1072
|
+
username: string;
|
|
1073
|
+
email: string;
|
|
1074
|
+
age: number;
|
|
1075
|
+
created_at?: Date;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
async function main() {
|
|
1079
|
+
// 1. Connect
|
|
1080
|
+
const mango = new Mango();
|
|
1081
|
+
await mango.connect({
|
|
1082
|
+
host: "localhost",
|
|
1083
|
+
user: "root",
|
|
1084
|
+
password: "password",
|
|
1085
|
+
database: "myapp"
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
// 2. Create table
|
|
1089
|
+
const users = await mango.createTable<User>("users", {
|
|
1090
|
+
id: mango.types().int().autoIncrement().primaryKey(),
|
|
1091
|
+
username: mango.types().varchar(50).notNull().unique(),
|
|
1092
|
+
email: mango.types().varchar(100).notNull(),
|
|
1093
|
+
age: mango.types().int().notNull(),
|
|
1094
|
+
created_at: mango.types().timeStamp()
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
// 3. Insert sample data
|
|
1098
|
+
await users.insertOne({
|
|
1099
|
+
username: "alice",
|
|
1100
|
+
email: "alice@example.com",
|
|
1101
|
+
age: 25
|
|
1102
|
+
}).execute();
|
|
1103
|
+
|
|
1104
|
+
// 4. Query with filters
|
|
1105
|
+
const adults = await users
|
|
1106
|
+
.selectColumns(["username", "email", "age"])
|
|
1107
|
+
.where("age", ">=", 18)
|
|
1108
|
+
.orderBy("age")
|
|
1109
|
+
.sort(-1)
|
|
1110
|
+
.limit(10)
|
|
1111
|
+
.execute();
|
|
1112
|
+
|
|
1113
|
+
console.log("Adult users:", adults);
|
|
1114
|
+
|
|
1115
|
+
// 5. Update
|
|
1116
|
+
await users
|
|
1117
|
+
.update({ age: 26 })
|
|
1118
|
+
.where("username", "=", "alice")
|
|
1119
|
+
.execute();
|
|
1120
|
+
|
|
1121
|
+
// 6. Delete
|
|
1122
|
+
await users
|
|
1123
|
+
.delete()
|
|
1124
|
+
.where("age", "<", 13)
|
|
1125
|
+
.execute();
|
|
1126
|
+
|
|
1127
|
+
// 7. Cleanup
|
|
1128
|
+
await mango.disconnect();
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
main().catch(console.error);
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
---
|
|
1135
|
+
|
|
1136
|
+
## ๐ค Contributing
|
|
1137
|
+
|
|
1138
|
+
We welcome contributions! This project is in active development and there are many ways to help:
|
|
1139
|
+
|
|
1140
|
+
### Ways to Contribute
|
|
1141
|
+
|
|
1142
|
+
- ๐ **Report Bugs**: Open an issue with reproduction steps
|
|
1143
|
+
- ๐ก **Suggest Features**: Share your ideas for new features
|
|
1144
|
+
- ๐ **Improve Documentation**: Fix typos, add examples, clarify concepts
|
|
1145
|
+
- ๐ง **Submit Pull Requests**: Fix bugs or implement features
|
|
1146
|
+
- โญ **Star the Project**: Show your support on GitHub
|
|
1147
|
+
- ๐ข **Spread the Word**: Share Mango with others
|
|
1148
|
+
|
|
1149
|
+
### Development Setup
|
|
1150
|
+
|
|
1151
|
+
```bash
|
|
1152
|
+
# Fork and clone the repository
|
|
1153
|
+
git clone https://github.com/YOUR_USERNAME/Mango.git
|
|
1154
|
+
cd Mango
|
|
1155
|
+
|
|
1156
|
+
# Install dependencies
|
|
1157
|
+
pnpm install
|
|
1158
|
+
|
|
1159
|
+
# Make your changes in src/
|
|
1160
|
+
|
|
1161
|
+
# Test your changes
|
|
1162
|
+
pnpm run test
|
|
1163
|
+
|
|
1164
|
+
# Build TypeScript
|
|
1165
|
+
pnpm run build
|
|
1166
|
+
|
|
1167
|
+
# Commit and push
|
|
1168
|
+
git add .
|
|
1169
|
+
git commit -m "Description of changes"
|
|
1170
|
+
git push origin your-branch-name
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
### Guidelines
|
|
1174
|
+
|
|
1175
|
+
- โ
Write clear commit messages
|
|
1176
|
+
- โ
Add tests for new features
|
|
1177
|
+
- โ
Update documentation for API changes
|
|
1178
|
+
- โ
Follow existing code style (use Prettier)
|
|
1179
|
+
- โ
Keep PRs focused on single features/fixes
|
|
1180
|
+
|
|
1181
|
+
### Code of Conduct
|
|
1182
|
+
|
|
1183
|
+
Be respectful, inclusive, and constructive. We're here to build something great together!
|
|
1184
|
+
|
|
1185
|
+
---
|
|
1186
|
+
|
|
1187
|
+
## ๐ License
|
|
1188
|
+
|
|
1189
|
+
**ISC License**
|
|
1190
|
+
|
|
1191
|
+
Copyright (c) 2024 Siddharth Karn
|
|
1192
|
+
|
|
1193
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
1194
|
+
|
|
1195
|
+
---
|
|
1196
|
+
|
|
1197
|
+
## ๐ค Author
|
|
1198
|
+
|
|
1199
|
+
**Siddharth Karn**
|
|
1200
|
+
|
|
1201
|
+
- GitHub: [@devSiddharthKarn](https://github.com/devSiddharthKarn)
|
|
1202
|
+
- Project: [Mango ORM](https://github.com/devSiddharthKarn/Mango)
|
|
1203
|
+
|
|
1204
|
+
---
|
|
1205
|
+
|
|
1206
|
+
## ๐ Acknowledgments
|
|
1207
|
+
|
|
1208
|
+
- Inspired by popular ORMs like Prisma, Sequelize, and TypeORM
|
|
1209
|
+
- Built with โค๏ธ using TypeScript
|
|
1210
|
+
- Community feedback and contributions
|
|
1211
|
+
|
|
1212
|
+
---
|
|
1213
|
+
|
|
1214
|
+
## ๐ Support & Community
|
|
1215
|
+
|
|
1216
|
+
- ๐ **Issues**: [GitHub Issues](https://github.com/devSiddharthKarn/Mango/issues)
|
|
1217
|
+
- ๐ฌ **Discussions**: [GitHub Discussions](https://github.com/devSiddharthKarn/Mango/discussions)
|
|
1218
|
+
- ๐ง **Email**: [Create an issue for private matters]
|
|
1219
|
+
|
|
1220
|
+
---
|
|
1221
|
+
|
|
1222
|
+
## โ ๏ธ Disclaimer
|
|
1223
|
+
|
|
1224
|
+
**This is an educational project currently under active development.**
|
|
1225
|
+
|
|
1226
|
+
- Not recommended for production use yet
|
|
1227
|
+
- API may change in future versions
|
|
1228
|
+
- No stability guarantees until v1.0.0
|
|
1229
|
+
- Use at your own risk
|
|
1230
|
+
- Always backup your data
|
|
1231
|
+
|
|
1232
|
+
---
|
|
1233
|
+
|
|
1234
|
+
<div align="center">
|
|
1235
|
+
|
|
1236
|
+
**Made with ๐ฅญ by Siddharth Karn**
|
|
1237
|
+
*Eat more mangoes ๐ฅญ๐ฅญ๐ฅญ!*
|
|
1238
|
+
|
|
1239
|
+
If you find this project helpful, please give it a โญ on [GitHub](https://github.com/devSiddharthKarn/Mango)!
|
|
1240
|
+
|
|
1241
|
+
[Report Bug](https://github.com/devSiddharthKarn/Mango/issues) โข [Request Feature](https://github.com/devSiddharthKarn/Mango/issues) โข [Contribute](https://github.com/devSiddharthKarn/Mango/pulls)
|
|
1242
|
+
|
|
1243
|
+
</div>
|
|
1244
|
+
|