@arcaelas/dynamite 1.0.23 → 1.0.24

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.
Files changed (2) hide show
  1. package/README.md +240 -1051
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,10 +1,6 @@
1
- ![Arcaelas Insiders](https://raw.githubusercontent.com/arcaelas/dist/main/banner/svg/dark.svg#gh-dark-mode-only)
2
- ![Arcaelas Insiders](https://raw.githubusercontent.com/arcaelas/dist/main/banner/svg/light.svg#gh-light-mode-only)
3
-
4
- # @arcaelas/dynamite
5
-
6
- > **A modern, decorator-first ORM for DynamoDB with TypeScript support**
7
- > Full-featured • Type-safe • Relationship support • Auto table creation • Zero boilerplate
1
+ <p align="center">
2
+ <img src="docs/assets/cover.png" alt="Dynamite ORM - Arcaelas Insiders for DynamoDB" width="100%">
3
+ </p>
8
4
 
9
5
  <p align="center">
10
6
  <a href="https://www.npmjs.com/package/@arcaelas/dynamite"><img src="https://img.shields.io/npm/v/@arcaelas/dynamite?color=cb3837" alt="npm"></a>
@@ -14,46 +10,17 @@
14
10
  <img src="https://img.shields.io/badge/TypeScript-5.x-blue" alt="TypeScript">
15
11
  </p>
16
12
 
17
- ---
13
+ # @arcaelas/dynamite
18
14
 
19
- ## 📚 Table of Contents
20
-
21
- - [🚀 Quick Start](#-quick-start)
22
- - [📦 Installation](#-installation)
23
- - [⚡ Basic Usage](#-basic-usage)
24
- - [🎯 Decorators Reference](#-decorators-reference)
25
- - [🔍 Query Operations](#-query-operations)
26
- - [🔗 Relationships](#-relationships)
27
- - [📝 TypeScript Types](#-typescript-types)
28
- - [🛠️ Advanced Features](#-advanced-features)
29
- - [⚙️ Configuration](#-configuration)
30
- - [📖 API Reference](#-api-reference)
31
- - [🔧 Development Setup](#-development-setup)
32
- - [❓ Troubleshooting](#-troubleshooting)
15
+ > **A modern, decorator-first ORM for DynamoDB with TypeScript support**
16
+ > Full-featured | Type-safe | Relationships | Auto table creation | Transactions
33
17
 
34
18
  ---
35
19
 
36
- ## 🚀 Quick Start
20
+ ## Quick Start
37
21
 
38
22
  ```typescript
39
- import {
40
- Table,
41
- PrimaryKey,
42
- Default,
43
- CreatedAt,
44
- UpdatedAt,
45
- CreationOptional,
46
- NonAttribute
47
- } from "@arcaelas/dynamite";
48
- import { Dynamite } from "@arcaelas/dynamite";
49
-
50
- // Configure connection
51
- Dynamite.config({
52
- region: "us-east-1",
53
- // For local development
54
- endpoint: "http://localhost:8000",
55
- credentials: { accessKeyId: "test", secretAccessKey: "test" }
56
- });
23
+ import { Dynamite, Table, PrimaryKey, Default, CreatedAt, UpdatedAt, CreationOptional } from "@arcaelas/dynamite";
57
24
 
58
25
  // Define your model
59
26
  class User extends Table<User> {
@@ -61,1212 +28,434 @@ class User extends Table<User> {
61
28
  @Default(() => crypto.randomUUID())
62
29
  declare id: CreationOptional<string>;
63
30
 
64
- @Default(() => "")
65
- declare name: CreationOptional<string>;
31
+ declare name: string;
32
+ declare email: string;
66
33
 
67
34
  @Default(() => "customer")
68
35
  declare role: CreationOptional<string>;
69
36
 
70
37
  @CreatedAt()
71
- declare createdAt: CreationOptional<string>;
72
-
73
- @UpdatedAt()
74
- declare updatedAt: CreationOptional<string>;
75
-
76
- // Computed property (not stored in database)
77
- declare displayName: NonAttribute<string>;
78
-
79
- constructor(data?: any) {
80
- super(data);
81
-
82
- // Define computed property
83
- Object.defineProperty(this, 'displayName', {
84
- get: () => `${this.name} (${this.role})`,
85
- enumerable: true
86
- });
87
- }
38
+ declare created_at: CreationOptional<string>;
39
+
40
+ @UpdatedAt()
41
+ declare updated_at: CreationOptional<string>;
88
42
  }
89
43
 
90
- // Use it!
91
- const user = await User.create({
92
- name: "John Doe"
93
- // id, role, createdAt, updatedAt are optional (CreationOptional)
44
+ // Connect to DynamoDB
45
+ const dynamite = new Dynamite({
46
+ region: "us-east-1",
47
+ endpoint: "http://localhost:8000", // DynamoDB Local
48
+ credentials: { accessKeyId: "test", secretAccessKey: "test" },
49
+ tables: [User]
94
50
  });
95
51
 
96
- console.log(user.name); // "John Doe"
97
- console.log(user.role); // "customer"
98
- console.log(user.displayName); // "John Doe (customer)"
99
- console.log(user.createdAt); // "2023-12-01T10:30:00.000Z"
52
+ await dynamite.connect();
53
+
54
+ // Use it!
55
+ const user = await User.create({ name: "John Doe", email: "john@example.com" });
56
+ console.log(user.id); // "a1b2c3d4-..."
57
+ console.log(user.role); // "customer"
58
+ console.log(user.created_at); // "2025-01-15T10:30:00.000Z"
100
59
  ```
101
60
 
102
61
  ---
103
62
 
104
- ## 📦 Installation
63
+ ## Installation
105
64
 
106
65
  ```bash
107
66
  npm install @arcaelas/dynamite
108
-
109
- # Peer dependencies (if not already installed)
110
- npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
111
67
  ```
112
68
 
113
69
  ---
114
70
 
115
- ## ⚡ Basic Usage
71
+ ## Decorators
116
72
 
117
- ### Table Definition
73
+ ### Index Decorators
118
74
 
119
- ```typescript
120
- import {
121
- Table,
122
- PrimaryKey,
123
- Default,
124
- Validate,
125
- Mutate,
126
- NotNull,
127
- Name
128
- } from "@arcaelas/dynamite";
75
+ | Decorator | Description |
76
+ |-----------|-------------|
77
+ | `@PrimaryKey()` | Primary key (partition key) |
78
+ | `@Index()` | Partition key for GSI |
79
+ | `@IndexSort()` | Sort key |
129
80
 
130
- @Name("custom_users") // Override table name
131
- class User extends Table<User> {
132
- @PrimaryKey()
133
- declare id: string;
81
+ ### Data Decorators
134
82
 
135
- @NotNull()
136
- @Mutate((value) => (value as string).toLowerCase().trim())
137
- @Validate((value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value as string) || "Invalid email")
138
- declare email: string;
83
+ | Decorator | Description |
84
+ |-----------|-------------|
85
+ | `@Default(value \| fn)` | Default value (static or dynamic) |
86
+ | `@Mutate(fn)` | Transform value before save |
87
+ | `@Validate(fn)` | Validate value before save |
88
+ | `@Serialize(fromDB, toDB)` | Bidirectional transformation |
89
+ | `@NotNull()` | Required field validation |
90
+ | `@Name("custom")` | Custom column/table name |
91
+ | `@Column()` | Column configuration |
139
92
 
140
- @Default(() => "")
141
- declare name: string;
93
+ ### Timestamp Decorators
142
94
 
143
- @Default(() => 18)
144
- @Validate((value) => (value as number) >= 0 || "Age must be positive")
145
- declare age: number;
95
+ | Decorator | Description |
96
+ |-----------|-------------|
97
+ | `@CreatedAt()` | Auto-set on creation |
98
+ | `@UpdatedAt()` | Auto-set on every update |
99
+ | `@DeleteAt()` | Soft delete timestamp |
146
100
 
147
- @Default(() => true)
148
- declare active: boolean;
101
+ ### Relationship Decorators
149
102
 
150
- @CreatedAt()
151
- declare createdAt: string;
103
+ | Decorator | Description |
104
+ |-----------|-------------|
105
+ | `@HasMany(() => Model, "foreign_key")` | One-to-many |
106
+ | `@HasOne(() => Model, "foreign_key")` | One-to-one |
107
+ | `@BelongsTo(() => Model, "local_key")` | Many-to-one |
108
+ | `@ManyToMany(() => Model, config)` | Many-to-many with pivot table |
152
109
 
153
- @UpdatedAt()
154
- declare updatedAt: string;
155
- }
156
- ```
110
+ ---
157
111
 
158
- ### CRUD Operations
112
+ ## TypeScript Types
159
113
 
160
114
  ```typescript
161
- // CREATE
162
- const user = await User.create({
163
- id: "user-123",
164
- email: "john@example.com",
165
- name: "John Doe",
166
- age: 25
167
- });
168
-
169
- // READ
170
- const allUsers = await User.where({});
171
- const activeUsers = await User.where({ active: true });
172
- const userById = await User.first({ id: "user-123" });
173
-
174
- // UPDATE
175
- await User.update("user-123", { name: "John Smith" });
176
- // or
177
- user.name = "John Smith";
178
- await user.save();
179
-
180
- // DELETE
181
- await User.delete("user-123");
182
- // or
183
- await user.destroy();
115
+ import {
116
+ CreationOptional, // Optional during create(), required after
117
+ NonAttribute, // Excluded from database (computed/relations)
118
+ InferAttributes, // Extract DB attributes from model
119
+ InferRelations, // Extract relations from model
120
+ CreateInput, // Input type for create()
121
+ UpdateInput, // Input type for update()
122
+ WhereOptions, // Query options type
123
+ QueryOperator // Available operators
124
+ } from "@arcaelas/dynamite";
184
125
  ```
185
126
 
186
- ---
127
+ ### CreationOptional
187
128
 
188
- ## 🎯 Decorators Reference
129
+ Use for fields that are optional during creation but exist after:
189
130
 
190
- ### Core Decorators
131
+ ```typescript
132
+ class User extends Table<User> {
133
+ @PrimaryKey()
134
+ @Default(() => crypto.randomUUID())
135
+ declare id: CreationOptional<string>; // Optional in create()
191
136
 
192
- | Decorator | Purpose | Example |
193
- |-----------|---------|---------|
194
- | `@PrimaryKey()` | Primary key (partition key) | `@PrimaryKey() declare id: string;` |
195
- | `@Index()` | Partition key (alias for PrimaryKey) | `@Index() declare userId: string;` |
196
- | `@IndexSort()` | Sort key | `@IndexSort() declare timestamp: string;` |
197
- | `@Name("custom")` | Custom column/table name | `@Name("user_email") declare email: string;` |
137
+ declare name: string; // Required in create()
198
138
 
199
- ### Data Decorators
139
+ @CreatedAt()
140
+ declare created_at: CreationOptional<string>; // Auto-generated
141
+ }
142
+ ```
200
143
 
201
- | Decorator | Purpose | Example |
202
- |-----------|---------|---------|
203
- | `@Default(value\|fn)` | Default value | `@Default(() => uuid()) declare id: string;` |
204
- | `@Mutate(fn)` | Transform value | `@Mutate((v) => v.toLowerCase()) declare email: string;` |
205
- | `@Validate(fn)` | Validation function | `@Validate((v) => v.length > 0 \|\| "Required") declare name: string;` |
206
- | `@NotNull()` | Not null validation | `@NotNull() declare email: string;` |
144
+ ### NonAttribute
207
145
 
208
- ### Timestamp Decorators
146
+ Use for computed properties and relations (not stored in DB):
209
147
 
210
- | Decorator | Purpose | Example |
211
- |-----------|---------|---------|
212
- | `@CreatedAt()` | Set on creation | `@CreatedAt() declare createdAt: string;` |
213
- | `@UpdatedAt()` | Set on every update | `@UpdatedAt() declare updatedAt: string;` |
148
+ ```typescript
149
+ class User extends Table<User> {
150
+ declare first_name: string;
151
+ declare last_name: string;
214
152
 
215
- ### Relationship Decorators
153
+ // Computed property - not stored
154
+ declare full_name: NonAttribute<string>;
216
155
 
217
- | Decorator | Purpose | Example |
218
- |-----------|---------|---------|
219
- | `@HasMany(Model, foreignKey)` | One-to-many | `@HasMany(() => Order, "user_id") declare orders: any;` |
220
- | `@BelongsTo(Model, localKey)` | Many-to-one | `@BelongsTo(() => User, "user_id") declare user: any;` |
156
+ // Relations - loaded via include
157
+ @HasMany(() => Order, "user_id")
158
+ declare orders: NonAttribute<Order[]>;
159
+ }
160
+ ```
221
161
 
222
162
  ---
223
163
 
224
- ## 🔍 Query Operations
164
+ ## Query Operations
225
165
 
226
166
  ### Basic Queries
227
167
 
228
168
  ```typescript
229
- // Get all records
169
+ // Get all
230
170
  const users = await User.where({});
231
171
 
232
172
  // Filter by field
233
- const activeUsers = await User.where({ active: true });
234
- const johnUsers = await User.where({ name: "John" });
173
+ const admins = await User.where({ role: "admin" });
174
+ const user = await User.where("email", "john@example.com");
235
175
 
236
- // Get first/last record
237
- const firstUser = await User.first({ active: true });
238
- const lastUser = await User.last({ active: true });
176
+ // First/Last
177
+ const first = await User.first({ active: true });
178
+ const last = await User.last({});
239
179
  ```
240
180
 
241
- ### Advanced Queries with Operators
181
+ ### Query Operators
242
182
 
243
183
  ```typescript
244
- // Comparison operators
245
- const adults = await User.where("age", ">=", 18);
246
- const youngAdults = await User.where("age", "<", 30);
247
- const specificAges = await User.where("age", "in", [25, 30, 35]);
248
- const excludeAges = await User.where("age", "not-in", [16, 17]);
249
-
250
- // String operators
251
- const gmailUsers = await User.where("email", "contains", "gmail");
252
- const usersByPrefix = await User.where("name", "begins-with", "John");
253
-
254
- // Not equal
255
- const nonAdmins = await User.where("role", "!=", "admin");
184
+ // Comparison
185
+ await User.where("age", ">=", 18);
186
+ await User.where("age", "<", 65);
187
+ await User.where("status", "!=", "banned");
188
+
189
+ // Array membership
190
+ await User.where("role", "in", ["admin", "moderator"]);
191
+
192
+ // String contains
193
+ await User.where("email", "$include", "gmail");
256
194
  ```
257
195
 
196
+ **Available operators:** `=`, `!=`, `<>`, `<`, `<=`, `>`, `>=`, `in`, `$include`
197
+
258
198
  ### Query Options
259
199
 
260
200
  ```typescript
261
- // Pagination and limiting
262
201
  const users = await User.where({}, {
263
202
  limit: 10,
264
- skip: 20
265
- });
266
-
267
- // Sorting
268
- const users = await User.where({}, {
269
- order: "ASC" // or "DESC"
270
- });
271
-
272
- // Select specific attributes
273
- const users = await User.where({}, {
274
- attributes: ["id", "name", "email"]
275
- });
276
- ```
277
-
278
- ### Method Chaining Alternative
279
-
280
- ```typescript
281
- // Using query builder style
282
- const users = await User
283
- .where("age", ">=", 18)
284
- .where("active", true);
285
-
286
- // Complex conditions
287
- const users = await User.where({
288
- age: 25,
289
- active: true,
290
- role: "customer"
203
+ skip: 20,
204
+ order: "DESC",
205
+ attributes: ["id", "name", "email"],
206
+ include: {
207
+ orders: {
208
+ where: { status: "completed" },
209
+ limit: 5
210
+ }
211
+ }
291
212
  });
292
213
  ```
293
214
 
294
215
  ---
295
216
 
296
- ## 🔗 Relationships
217
+ ## Relationships
297
218
 
298
- ### Defining Relationships
219
+ ### Defining Relations
299
220
 
300
221
  ```typescript
301
- // User model
302
222
  class User extends Table<User> {
303
223
  @PrimaryKey()
304
224
  declare id: string;
305
225
 
306
226
  @HasMany(() => Order, "user_id")
307
- declare orders: any;
227
+ declare orders: NonAttribute<Order[]>;
228
+
229
+ @HasOne(() => Profile, "user_id")
230
+ declare profile: NonAttribute<Profile | null>;
308
231
 
309
- @HasMany(() => Review, "user_id")
310
- declare reviews: any;
232
+ @ManyToMany(() => Role, {
233
+ pivotTable: "user_roles",
234
+ foreignKey: "user_id",
235
+ relatedKey: "role_id"
236
+ })
237
+ declare roles: NonAttribute<Role[]>;
311
238
  }
312
239
 
313
- // Order model
314
240
  class Order extends Table<Order> {
315
241
  @PrimaryKey()
316
242
  declare id: string;
317
243
 
318
- @NotNull()
319
244
  declare user_id: string;
320
245
 
321
246
  @BelongsTo(() => User, "user_id")
322
- declare user: any;
323
-
324
- @HasMany(() => OrderItem, "order_id")
325
- declare items: any;
326
- }
327
-
328
- // OrderItem model
329
- class OrderItem extends Table<OrderItem> {
330
- @PrimaryKey()
331
- declare id: string;
332
-
333
- @NotNull()
334
- declare order_id: string;
335
-
336
- @NotNull()
337
- declare product_id: string;
338
-
339
- @BelongsTo(() => Order, "order_id")
340
- declare order: any;
341
-
342
- @BelongsTo(() => Product, "product_id")
343
- declare product: any;
247
+ declare user: NonAttribute<User | null>;
344
248
  }
345
249
  ```
346
250
 
347
- ### Loading Relationships
251
+ ### Loading Relations
348
252
 
349
253
  ```typescript
350
- // Load with relationships
351
- const usersWithOrders = await User.where({}, {
352
- include: {
353
- orders: {}
354
- }
355
- });
356
-
357
- // Nested relationships
358
- const usersWithCompleteData = await User.where({}, {
359
- include: {
360
- orders: {
361
- include: {
362
- items: {
363
- include: {
364
- product: {}
365
- }
366
- }
367
- }
368
- }
369
- }
370
- });
371
-
372
- // Filtered relationships
373
- const usersWithRecentOrders = await User.where({}, {
374
- include: {
375
- orders: {
376
- where: { status: "completed" },
377
- limit: 5,
378
- order: "DESC"
379
- }
380
- }
381
- });
382
-
383
- // Relationship with specific attributes
384
- const usersWithOrderSummary = await User.where({}, {
254
+ const users = await User.where({}, {
385
255
  include: {
386
- orders: {
387
- attributes: ["id", "total", "status"],
388
- where: { status: "completed" }
389
- }
256
+ orders: { where: { status: "completed" } },
257
+ profile: {},
258
+ roles: {}
390
259
  }
391
260
  });
392
261
  ```
393
262
 
394
- ---
395
-
396
- ## 📝 TypeScript Types
397
-
398
- Dynamite provides essential TypeScript types that are fundamental for proper model definition and type safety. These types help you define optional fields, exclude computed properties, and establish relationships.
399
-
400
- ### Core Types
401
-
402
- #### `CreationOptional<T>`
403
-
404
- Marks a field as optional during creation but required in the actual model instance. **Always use for auto-generated fields**: `id` (with @PrimaryKey), `createdAt` (@CreatedAt), `updatedAt` (@UpdatedAt), and any field with @Default decorator.
263
+ ### ManyToMany Operations
405
264
 
406
265
  ```typescript
407
- import { Table, PrimaryKey, Default, CreatedAt, UpdatedAt, CreationOptional } from "@arcaelas/dynamite";
408
-
409
- class User extends Table<User> {
410
- // Always CreationOptional - auto-generated ID
411
- @PrimaryKey()
412
- @Default(() => crypto.randomUUID())
413
- declare id: CreationOptional<string>;
266
+ const user = await User.first({ id: "user-1" });
414
267
 
415
- // Required fields during creation
416
- declare name: string;
417
- declare email: string;
268
+ // Attach relation
269
+ await user.attach(Role, "role-123");
418
270
 
419
- // Always CreationOptional - has default value
420
- @Default(() => "customer")
421
- declare role: CreationOptional<string>;
271
+ // Detach relation
272
+ await user.detach(Role, "role-123");
422
273
 
423
- // Always CreationOptional - auto-set timestamps
424
- @CreatedAt()
425
- declare createdAt: CreationOptional<string>;
426
-
427
- @UpdatedAt()
428
- declare updatedAt: CreationOptional<string>;
429
- }
430
-
431
- // Usage - TypeScript knows exactly what's required
432
- const user = await User.create({
433
- name: "John Doe", // Required
434
- email: "john@test.com" // Required
435
- // id, role, createdAt, updatedAt are automatically optional
436
- });
274
+ // Sync relations (replace all)
275
+ await user.sync(Role, ["role-1", "role-2", "role-3"]);
437
276
  ```
438
277
 
439
- **Rule of thumb**: Use `CreationOptional<T>` for:
440
- - `@PrimaryKey()` with `@Default()` → Always optional
441
- - `@CreatedAt()` → Always optional
442
- - `@UpdatedAt()` → Always optional
443
- - Any field with `@Default()` → Always optional
278
+ ---
444
279
 
445
- #### `NonAttribute<T>`
280
+ ## CRUD Operations
446
281
 
447
- Excludes a field from database operations while keeping it in the TypeScript interface. Used for computed properties, getters, or virtual fields.
282
+ ### Create
448
283
 
449
284
  ```typescript
450
- import { Table, PrimaryKey, NonAttribute } from "@arcaelas/dynamite";
451
-
452
- class User extends Table<User> {
453
- @PrimaryKey()
454
- declare id: string;
455
-
456
- declare firstName: string;
457
- declare lastName: string;
458
- declare birthDate: string;
459
-
460
- // Computed property - not stored in database
461
- declare fullName: NonAttribute<string>;
462
- declare age: NonAttribute<number>;
463
-
464
- // Getter methods as non-attributes
465
- declare getDisplayName: NonAttribute<() => string>;
466
-
467
- constructor(data?: any) {
468
- super(data);
469
-
470
- // Define computed properties
471
- Object.defineProperty(this, 'fullName', {
472
- get: () => `${this.firstName} ${this.lastName}`,
473
- enumerable: true
474
- });
475
-
476
- Object.defineProperty(this, 'age', {
477
- get: () => {
478
- const today = new Date();
479
- const birth = new Date(this.birthDate);
480
- return today.getFullYear() - birth.getFullYear();
481
- },
482
- enumerable: true
483
- });
484
-
485
- Object.defineProperty(this, 'getDisplayName', {
486
- value: () => this.fullName.toUpperCase(),
487
- enumerable: false
488
- });
489
- }
490
- }
491
-
492
- // Usage
493
285
  const user = await User.create({
494
- id: "user-1",
495
- firstName: "John",
496
- lastName: "Doe",
497
- birthDate: "1990-01-01"
286
+ name: "John Doe",
287
+ email: "john@example.com"
498
288
  });
499
-
500
- console.log(user.fullName); // "John Doe" (not stored in DB)
501
- console.log(user.age); // 34 (computed)
502
- console.log(user.getDisplayName()); // "JOHN DOE"
503
289
  ```
504
290
 
505
- ### Relationship Types
506
-
507
- #### `HasMany<T>`
508
-
509
- Defines a one-to-many relationship where the model can have multiple related instances.
291
+ ### Read
510
292
 
511
293
  ```typescript
512
- import { Table, PrimaryKey, HasMany, NonAttribute } from "@arcaelas/dynamite";
513
-
514
- class User extends Table<User> {
515
- @PrimaryKey()
516
- declare id: string;
517
-
518
- declare name: string;
519
- declare email: string;
520
-
521
- // One-to-many: User has many Orders
522
- @HasMany(() => Order, "user_id")
523
- declare orders: NonAttribute<HasMany<Order>>;
524
-
525
- // One-to-many: User has many Reviews
526
- @HasMany(() => Review, "user_id")
527
- declare reviews: NonAttribute<HasMany<Review>>;
528
- }
529
-
530
- class Order extends Table<Order> {
531
- @PrimaryKey()
532
- declare id: string;
533
-
534
- declare user_id: string;
535
- declare total: number;
536
- declare status: string;
537
- }
538
-
539
- class Review extends Table<Review> {
540
- @PrimaryKey()
541
- declare id: string;
542
-
543
- declare user_id: string;
544
- declare rating: number;
545
- declare comment: string;
546
- }
547
-
548
- // Usage
549
- const userWithOrders = await User.where({ id: "user-1" }, {
550
- include: {
551
- orders: {
552
- where: { status: "completed" },
553
- limit: 10
554
- },
555
- reviews: {
556
- where: { rating: { $gte: 4 } }
557
- }
558
- }
559
- });
560
-
561
- // TypeScript knows these are arrays
562
- console.log(userWithOrders[0].orders.length); // number
563
- console.log(userWithOrders[0].reviews[0].rating); // number
294
+ const users = await User.where({ active: true });
295
+ const user = await User.first({ id: "user-123" });
564
296
  ```
565
297
 
566
- #### `BelongsTo<T>`
567
-
568
- Defines a many-to-one relationship where the model belongs to a single parent instance.
298
+ ### Update
569
299
 
570
300
  ```typescript
571
- import { Table, PrimaryKey, BelongsTo, NonAttribute } from "@arcaelas/dynamite";
301
+ // Static update (bulk)
302
+ await User.update({ role: "premium" }, { id: "user-123" });
572
303
 
573
- class Order extends Table<Order> {
574
- @PrimaryKey()
575
- declare id: string;
576
-
577
- // Foreign key
578
- @NotNull()
579
- declare user_id: string;
580
-
581
- @NotNull()
582
- declare category_id: string;
583
-
584
- declare total: number;
585
- declare status: string;
586
-
587
- // Many-to-one: Order belongs to User
588
- @BelongsTo(() => User, "user_id")
589
- declare user: NonAttribute<BelongsTo<User>>;
590
-
591
- // Many-to-one: Order belongs to Category
592
- @BelongsTo(() => Category, "category_id")
593
- declare category: NonAttribute<BelongsTo<Category>>;
594
- }
595
-
596
- class User extends Table<User> {
597
- @PrimaryKey()
598
- declare id: string;
599
-
600
- declare name: string;
601
- declare email: string;
602
- }
603
-
604
- class Category extends Table<Category> {
605
- @PrimaryKey()
606
- declare id: string;
607
-
608
- declare name: string;
609
- declare description: string;
610
- }
611
-
612
- // Usage
613
- const orderWithRelations = await Order.where({ id: "order-1" }, {
614
- include: {
615
- user: {
616
- attributes: ["id", "name", "email"]
617
- },
618
- category: {}
619
- }
620
- });
304
+ // Instance update
305
+ user.name = "Jane Doe";
306
+ await user.save();
621
307
 
622
- // TypeScript knows these can be null or the related type
623
- if (orderWithRelations[0].user) {
624
- console.log(orderWithRelations[0].user.name); // string
625
- }
626
- if (orderWithRelations[0].category) {
627
- console.log(orderWithRelations[0].category.name); // string
628
- }
308
+ // Or
309
+ await user.update({ name: "Jane Doe" });
629
310
  ```
630
311
 
631
- ### Advanced Type Combinations
632
-
633
- #### Complete Model Example
312
+ ### Delete
634
313
 
635
314
  ```typescript
636
- import {
637
- Table,
638
- PrimaryKey,
639
- Default,
640
- CreatedAt,
641
- UpdatedAt,
642
- HasMany,
643
- BelongsTo,
644
- CreationOptional,
645
- NonAttribute
646
- } from "@arcaelas/dynamite";
647
-
648
- class User extends Table<User> {
649
- // Always CreationOptional - auto-generated primary key
650
- @PrimaryKey()
651
- @Default(() => crypto.randomUUID())
652
- declare id: CreationOptional<string>;
653
-
654
- // Required fields during creation
655
- declare firstName: string;
656
- declare lastName: string;
657
- declare email: string;
658
-
659
- // Always CreationOptional - has default values
660
- @Default(() => "customer")
661
- declare role: CreationOptional<string>;
662
-
663
- @Default(() => true)
664
- declare active: CreationOptional<boolean>;
665
-
666
- // Always CreationOptional - auto-set timestamps
667
- @CreatedAt()
668
- declare createdAt: CreationOptional<string>;
669
-
670
- @UpdatedAt()
671
- declare updatedAt: CreationOptional<string>;
315
+ // Static delete (bulk)
316
+ await User.delete({ status: "inactive" });
672
317
 
673
- // Computed properties (not stored)
674
- declare fullName: NonAttribute<string>;
675
- declare displayRole: NonAttribute<string>;
676
-
677
- // Relationships (not stored directly)
678
- @HasMany(() => Order, "user_id")
679
- declare orders: NonAttribute<HasMany<Order>>;
680
-
681
- @HasMany(() => Review, "user_id")
682
- declare reviews: NonAttribute<HasMany<Review>>;
683
-
684
- constructor(data?: any) {
685
- super(data);
686
-
687
- // Define computed properties
688
- Object.defineProperty(this, 'fullName', {
689
- get: () => `${this.firstName} ${this.lastName}`,
690
- enumerable: true
691
- });
692
-
693
- Object.defineProperty(this, 'displayRole', {
694
- get: () => this.role.charAt(0).toUpperCase() + this.role.slice(1),
695
- enumerable: true
696
- });
697
- }
698
- }
699
-
700
- class Order extends Table<Order> {
701
- // Always CreationOptional - auto-generated ID
702
- @PrimaryKey()
703
- @Default(() => crypto.randomUUID())
704
- declare id: CreationOptional<string>;
705
-
706
- // Required field during creation
707
- declare user_id: string;
708
- declare total: number;
709
-
710
- // Always CreationOptional - has default value
711
- @Default(() => "pending")
712
- declare status: CreationOptional<string>;
713
-
714
- // Always CreationOptional - auto-set timestamp
715
- @CreatedAt()
716
- declare createdAt: CreationOptional<string>;
717
-
718
- // Relationship
719
- @BelongsTo(() => User, "user_id")
720
- declare user: NonAttribute<BelongsTo<User>>;
721
-
722
- // Computed total with tax
723
- declare totalWithTax: NonAttribute<number>;
724
-
725
- constructor(data?: any) {
726
- super(data);
727
-
728
- Object.defineProperty(this, 'totalWithTax', {
729
- get: () => this.total * 1.1, // 10% tax
730
- enumerable: true
731
- });
732
- }
733
- }
734
-
735
- // Perfect TypeScript inference
736
- const createUser = async () => {
737
- // TypeScript knows what's required vs optional
738
- const user = await User.create({
739
- firstName: "John", // required
740
- lastName: "Doe", // required
741
- email: "john@test.com" // required
742
- // id, role, active, createdAt, updatedAt are optional
743
- });
744
-
745
- // Computed properties work immediately
746
- console.log(user.fullName); // "John Doe"
747
- console.log(user.displayRole); // "Customer"
748
-
749
- return user;
750
- };
751
-
752
- // Load with relationships
753
- const getUserWithOrders = async (userId: string) => {
754
- const users = await User.where({ id: userId }, {
755
- include: {
756
- orders: {
757
- include: {
758
- user: {} // Recursive relationship
759
- }
760
- }
761
- }
762
- });
763
-
764
- const user = users[0];
765
- if (user?.orders?.length > 0) {
766
- console.log(`${user.fullName} has ${user.orders.length} orders`);
767
- user.orders.forEach(order => {
768
- console.log(`Order ${order.id}: $${order.totalWithTax}`);
769
- });
770
- }
771
-
772
- return user;
773
- };
774
- ```
775
-
776
- ### Type Inference Benefits
318
+ // Instance delete (soft delete if @DeleteAt present)
319
+ await user.destroy();
777
320
 
778
- ```typescript
779
- // TypeScript will infer all the correct types
780
- type UserCreationAttributes = {
781
- firstName: string; // Required
782
- lastName: string; // Required
783
- email: string; // Required
784
- // All these are automatically optional (CreationOptional):
785
- id?: string; // @PrimaryKey + @Default
786
- role?: string; // @Default
787
- active?: boolean; // @Default
788
- createdAt?: string; // @CreatedAt (always optional)
789
- updatedAt?: string; // @UpdatedAt (always optional)
790
- };
791
-
792
- type UserAttributes = {
793
- // All these exist in the instance (required after creation)
794
- id: string; // CreationOptional but exists after creation
795
- firstName: string;
796
- lastName: string;
797
- email: string;
798
- role: string; // CreationOptional but exists after creation
799
- active: boolean; // CreationOptional but exists after creation
800
- createdAt: string; // CreationOptional but exists after creation
801
- updatedAt: string; // CreationOptional but exists after creation
802
- fullName: string; // NonAttribute computed property
803
- displayRole: string; // NonAttribute computed property
804
- orders: Order[]; // HasMany relationship (NonAttribute)
805
- reviews: Review[]; // HasMany relationship (NonAttribute)
806
- };
807
-
808
- // Perfect type safety
809
- const user: UserAttributes = await User.create({
810
- firstName: "John",
811
- lastName: "Doe",
812
- email: "john@example.com"
813
- } satisfies UserCreationAttributes);
321
+ // Force hard delete
322
+ await user.forceDestroy();
814
323
  ```
815
324
 
816
325
  ---
817
326
 
818
- ## 🛠️ Advanced Features
819
-
820
- ### Data Validation and Transformation
327
+ ## Soft Deletes
821
328
 
822
329
  ```typescript
823
- class User extends Table<User> {
330
+ class Post extends Table<Post> {
824
331
  @PrimaryKey()
825
332
  declare id: string;
826
333
 
827
- // Multiple transformations (executed in order)
828
- @Mutate((value) => (value as string).trim())
829
- @Mutate((value) => (value as string).toLowerCase())
830
- @Validate((value) => /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/.test(value as string) || "Invalid email format")
831
- declare email: string;
832
-
833
- // Complex validation
834
- @Validate((value) => {
835
- const age = value as number;
836
- if (age < 0) return "Age cannot be negative";
837
- if (age > 150) return "Age seems unrealistic";
838
- return true;
839
- })
840
- declare age: number;
334
+ declare title: string;
841
335
 
842
- // Multiple validators
843
- @Validate((value) => (value as string).length >= 2 || "Name too short")
844
- @Validate((value) => (value as string).length <= 50 || "Name too long")
845
- @Validate((value) => /^[a-zA-Z\s]+$/.test(value as string) || "Name can only contain letters and spaces")
846
- declare name: string;
336
+ @DeleteAt()
337
+ declare deleted_at: CreationOptional<string | null>;
847
338
  }
848
- ```
849
339
 
850
- ### Custom Table Names
340
+ // Soft delete
341
+ await post.destroy(); // Sets deleted_at timestamp
851
342
 
852
- ```typescript
853
- // Table name override
854
- @Name("custom_table_name")
855
- class MyModel extends Table<MyModel> {
856
- @PrimaryKey()
857
- declare id: string;
858
-
859
- // Column name override
860
- @Name("custom_column")
861
- declare myField: string;
862
- }
863
- ```
343
+ // Query including soft-deleted
344
+ const all = await Post.withTrashed({});
864
345
 
865
- ### Complex Queries
346
+ // Query only soft-deleted
347
+ const trashed = await Post.onlyTrashed({});
866
348
 
867
- ```typescript
868
- // Multiple conditions
869
- const users = await User.where({
870
- age: 25,
871
- active: true,
872
- role: "premium"
873
- });
874
-
875
- // Range queries
876
- const users = await User.where("createdAt", ">=", "2023-01-01");
877
-
878
- // Array filtering
879
- const premiumUsers = await User.where("role", "in", ["admin", "premium", "vip"]);
880
-
881
- // Pattern matching
882
- const testUsers = await User.where("email", "contains", "@test.com");
349
+ // Force hard delete
350
+ await post.forceDestroy();
883
351
  ```
884
352
 
885
- ### Batch Operations
353
+ ---
354
+
355
+ ## Transactions
886
356
 
887
357
  ```typescript
888
- // Batch create
889
- const users = await Promise.all([
890
- User.create({ id: "1", name: "User 1" }),
891
- User.create({ id: "2", name: "User 2" }),
892
- User.create({ id: "3", name: "User 3" })
893
- ]);
894
-
895
- // Batch update
896
- await Promise.all(users.map(user => {
897
- user.active = false;
898
- return user.save();
899
- }));
358
+ await dynamite.tx(async (tx) => {
359
+ const user = await User.create({ name: "John" }, tx);
360
+ await Order.create({ user_id: user.id, total: 100 }, tx);
361
+ // If any operation fails, all are rolled back
362
+ });
900
363
  ```
901
364
 
902
365
  ---
903
366
 
904
- ## ⚙️ Configuration
367
+ ## Configuration
905
368
 
906
- ### Connection Setup
369
+ ### DynamoDB Local
907
370
 
908
371
  ```typescript
909
- import { Dynamite } from "@arcaelas/dynamite";
910
-
911
- // AWS DynamoDB
912
- Dynamite.config({
913
- region: "us-east-1",
914
- credentials: {
915
- accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
916
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
917
- }
918
- });
919
-
920
- // DynamoDB Local
921
- Dynamite.config({
372
+ const dynamite = new Dynamite({
922
373
  region: "us-east-1",
923
374
  endpoint: "http://localhost:8000",
924
- credentials: {
925
- accessKeyId: "test",
926
- secretAccessKey: "test"
927
- }
375
+ credentials: { accessKeyId: "test", secretAccessKey: "test" },
376
+ tables: [User, Order, Product]
928
377
  });
929
378
 
930
- // With custom configuration
931
- Dynamite.config({
932
- region: "us-east-1",
933
- endpoint: "https://dynamodb.us-east-1.amazonaws.com",
934
- credentials: {
935
- accessKeyId: "your-key",
936
- secretAccessKey: "your-secret"
937
- },
938
- maxAttempts: 3,
939
- requestTimeout: 3000
940
- });
379
+ await dynamite.connect();
941
380
  ```
942
381
 
943
- ### Environment Variables
944
-
945
- ```bash
946
- # .env file
947
- AWS_REGION=us-east-1
948
- AWS_ACCESS_KEY_ID=your-access-key
949
- AWS_SECRET_ACCESS_KEY=your-secret-key
950
- DYNAMODB_ENDPOINT=http://localhost:8000 # for local development
951
- ```
382
+ ### AWS DynamoDB
952
383
 
953
384
  ```typescript
954
- // Load from environment
955
- Dynamite.config({
956
- region: process.env.AWS_REGION!,
957
- endpoint: process.env.DYNAMODB_ENDPOINT,
385
+ const dynamite = new Dynamite({
386
+ region: "us-east-1",
958
387
  credentials: {
959
388
  accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
960
389
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
961
- }
390
+ },
391
+ tables: [User, Order, Product]
962
392
  });
393
+
394
+ await dynamite.connect();
963
395
  ```
964
396
 
965
- ### Docker Setup for Development
397
+ ### Docker Setup
966
398
 
967
399
  ```bash
968
- # Start DynamoDB Local
969
400
  docker run -d -p 8000:8000 amazon/dynamodb-local
970
-
971
- # Or with Docker Compose
972
- ```
973
-
974
- ```yaml
975
- # docker-compose.yml
976
- version: '3.8'
977
- services:
978
- dynamodb-local:
979
- image: amazon/dynamodb-local
980
- ports:
981
- - "8000:8000"
982
- command: ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", "/home/dynamodblocal/data/"]
983
- volumes:
984
- - dynamodb_data:/home/dynamodblocal/data
985
- working_dir: /home/dynamodblocal
986
-
987
- volumes:
988
- dynamodb_data:
989
401
  ```
990
402
 
991
403
  ---
992
404
 
993
- ## 📖 API Reference
405
+ ## API Reference
994
406
 
995
- ### Table Class Methods
996
-
997
- #### Static Methods
407
+ ### Static Methods
998
408
 
999
409
  ```typescript
1000
- // CRUD Operations
1001
- static async create<T>(data: Partial<InferAttributes<T>>): Promise<T>
1002
- static async update<T>(id: string, data: Partial<InferAttributes<T>>): Promise<T>
1003
- static async delete<T>(id: string): Promise<void>
1004
-
1005
- // Query Methods
1006
- static async where<T>(filters?: Partial<InferAttributes<T>>, options?: WhereOptions<T>): Promise<T[]>
1007
- static async where<T>(field: keyof InferAttributes<T>, value: any): Promise<T[]>
1008
- static async where<T>(field: keyof InferAttributes<T>, operator: QueryOperator, value: any): Promise<T[]>
1009
-
1010
- static async first<T>(filters?: Partial<InferAttributes<T>>): Promise<T | undefined>
1011
- static async last<T>(filters?: Partial<InferAttributes<T>>): Promise<T | undefined>
1012
-
1013
- // Utility Methods
1014
- static async count<T>(filters?: Partial<InferAttributes<T>>): Promise<number>
1015
- static async exists<T>(id: string): Promise<boolean>
1016
- ```
410
+ // CRUD
411
+ static create(data, tx?): Promise<T>
412
+ static update(data, filters, tx?): Promise<number>
413
+ static delete(filters, tx?): Promise<number>
1017
414
 
1018
- #### Instance Methods
415
+ // Query
416
+ static where(filters, options?): Promise<T[]>
417
+ static where(field, value): Promise<T[]>
418
+ static where(field, operator, value): Promise<T[]>
419
+ static first(filters?, options?): Promise<T | undefined>
420
+ static last(filters?, options?): Promise<T | undefined>
1019
421
 
1020
- ```typescript
1021
- // CRUD Operations
1022
- async save(): Promise<this>
1023
- async update(data: Partial<InferAttributes<T>>): Promise<this>
1024
- async destroy(): Promise<void>
1025
- async reload(): Promise<this>
1026
-
1027
- // Serialization
1028
- toJSON(): Record<string, any>
422
+ // Soft deletes
423
+ static withTrashed(filters?, options?): Promise<T[]>
424
+ static onlyTrashed(filters?, options?): Promise<T[]>
1029
425
  ```
1030
426
 
1031
- ### Query Operators
1032
-
1033
- | Operator | Description | Example |
1034
- |----------|-------------|---------|
1035
- | `=` | Equal to (default) | `User.where("age", 25)` |
1036
- | `!=` | Not equal to | `User.where("status", "!=", "deleted")` |
1037
- | `<` | Less than | `User.where("age", "<", 18)` |
1038
- | `<=` | Less than or equal | `User.where("age", "<=", 65)` |
1039
- | `>` | Greater than | `User.where("score", ">", 100)` |
1040
- | `>=` | Greater than or equal | `User.where("age", ">=", 18)` |
1041
- | `in` | In array | `User.where("role", "in", ["admin", "user"])` |
1042
- | `not-in` | Not in array | `User.where("status", "not-in", ["banned", "deleted"])` |
1043
- | `contains` | String contains | `User.where("email", "contains", "gmail")` |
1044
- | `begins-with` | String starts with | `User.where("name", "begins-with", "John")` |
1045
-
1046
- ### Type Definitions
427
+ ### Instance Methods
1047
428
 
1048
429
  ```typescript
1049
- // Core Types - Essential for model definition
1050
- type InferAttributes<T> = {
1051
- [K in keyof T]: T[K] extends NonAttribute<any> ? never : T[K]
1052
- }
1053
-
1054
- type CreationOptional<T> = T
1055
- // Marks fields as optional during creation but required in instances
1056
- // ALWAYS use for: @PrimaryKey + @Default, @CreatedAt, @UpdatedAt, any @Default
1057
- // Example: @CreatedAt() declare createdAt: CreationOptional<string>
1058
-
1059
- type NonAttribute<T> = T
1060
- // Excludes fields from database operations
1061
- // Example: declare fullName: NonAttribute<string>
1062
-
1063
- // Relationship Types - Define model associations
1064
- type HasMany<T> = T[]
1065
- // One-to-many relationship: Parent has multiple children
1066
- // Example: @HasMany(() => Order, "user_id") declare orders: NonAttribute<HasMany<Order>>
1067
-
1068
- type BelongsTo<T> = T | null
1069
- // Many-to-one relationship: Child belongs to parent
1070
- // Example: @BelongsTo(() => User, "user_id") declare user: NonAttribute<BelongsTo<User>>
1071
-
1072
- // Query Types
1073
- type QueryOperator = "=" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "contains" | "begins-with"
1074
-
1075
- type WhereOptions<T> = {
1076
- limit?: number;
1077
- skip?: number;
1078
- order?: "ASC" | "DESC";
1079
- attributes?: (keyof InferAttributes<T>)[];
1080
- include?: {
1081
- [K in keyof T]?: T[K] extends NonAttribute<HasMany<any> | BelongsTo<any>>
1082
- ? IncludeOptions | {}
1083
- : never;
1084
- };
1085
- }
1086
-
1087
- type IncludeOptions = {
1088
- where?: Record<string, any>;
1089
- limit?: number;
1090
- order?: "ASC" | "DESC";
1091
- attributes?: string[];
1092
- include?: Record<string, IncludeOptions | {}>;
1093
- }
1094
-
1095
- // Creation and Update Types
1096
- type CreationAttributes<T> = {
1097
- [K in keyof InferAttributes<T>]: InferAttributes<T>[K] extends CreationOptional<infer U>
1098
- ? U | undefined
1099
- : InferAttributes<T>[K]
1100
- }
430
+ // CRUD
431
+ save(): Promise<boolean>
432
+ update(data): Promise<boolean>
433
+ destroy(): Promise<null>
434
+ forceDestroy(): Promise<null>
1101
435
 
1102
- type UpdateAttributes<T> = Partial<InferAttributes<T>>
1103
- ```
1104
-
1105
- ---
1106
-
1107
- ## 🔧 Development Setup
1108
-
1109
- ### Project Structure
1110
-
1111
- ```
1112
- src/
1113
- ├── core/
1114
- │ ├── client.ts # Dynamite client configuration
1115
- │ ├── table.ts # Base Table class
1116
- │ └── wrapper.ts # Metadata management
1117
- ├── decorators/
1118
- │ ├── index.ts # @Index decorator
1119
- │ ├── primary_key.ts # @PrimaryKey decorator
1120
- │ ├── default.ts # @Default decorator
1121
- │ ├── validate.ts # @Validate decorator
1122
- │ ├── mutate.ts # @Mutate decorator
1123
- │ ├── created_at.ts # @CreatedAt decorator
1124
- │ ├── updated_at.ts # @UpdatedAt decorator
1125
- │ ├── not_null.ts # @NotNull decorator
1126
- │ ├── name.ts # @Name decorator
1127
- │ ├── has_many.ts # @HasMany decorator
1128
- │ └── belongs_to.ts # @BelongsTo decorator
1129
- ├── utils/
1130
- │ ├── relations.ts # Relationship handling
1131
- │ ├── naming.ts # Table/column naming
1132
- │ └── projection.ts # Field projection
1133
- ├── @types/
1134
- │ └── index.ts # TypeScript definitions
1135
- └── index.ts # Public API exports
1136
- ```
1137
-
1138
- ### Running Tests
1139
-
1140
- ```bash
1141
- # Start DynamoDB Local
1142
- docker run -d -p 8000:8000 amazon/dynamodb-local
436
+ // ManyToMany
437
+ attach(Model, id, pivotData?): Promise<void>
438
+ detach(Model, id): Promise<void>
439
+ sync(Model, ids): Promise<void>
1143
440
 
1144
- # Run tests
1145
- npm test
1146
-
1147
- # Run specific test
1148
- npm test -- --testNamePattern="should handle relationships"
1149
-
1150
- # Run with coverage
1151
- npm test -- --coverage
1152
- ```
1153
-
1154
- ### Example Test
1155
-
1156
- ```typescript
1157
- describe("User Model", () => {
1158
- beforeEach(async () => {
1159
- // Setup test data
1160
- await User.create({
1161
- id: "test-user",
1162
- email: "test@example.com",
1163
- name: "Test User"
1164
- });
1165
- });
1166
-
1167
- it("should create user with defaults", async () => {
1168
- const user = await User.create({
1169
- id: "user-2",
1170
- email: "user2@example.com"
1171
- });
1172
-
1173
- expect(user.name).toBe("");
1174
- expect(user.active).toBe(true);
1175
- expect(user.createdAt).toBeDefined();
1176
- });
1177
-
1178
- it("should validate email format", async () => {
1179
- await expect(User.create({
1180
- id: "user-3",
1181
- email: "invalid-email"
1182
- })).rejects.toThrow("Invalid email");
1183
- });
1184
- });
441
+ // Serialization
442
+ toJSON(): Record<string, unknown>
1185
443
  ```
1186
444
 
1187
445
  ---
1188
446
 
1189
- ## ❓ Troubleshooting
1190
-
1191
- ### Common Errors
1192
-
1193
- | Error | Cause | Solution |
1194
- |-------|-------|----------|
1195
- | `Metadata no encontrada` | Model imported before decorators executed | Ensure `connect()` runs first, avoid circular imports |
1196
- | `PartitionKey faltante` | No `@PrimaryKey()` or `@Index()` in model | Add primary key decorator |
1197
- | `Two keys can not have the same name` | PK & SK attribute name clash | Use different column names |
1198
- | `UnrecognizedClientException` | Wrong credentials or DynamoDB Local not running | Check credentials, start DynamoDB Local |
1199
- | `ValidationException` | Invalid attribute names or values | Check for reserved keywords, validate data |
1200
-
1201
- ### Performance Tips
447
+ ## Documentation
1202
448
 
1203
- ```typescript
1204
- // Use attributes to limit returned data
1205
- const users = await User.where({}, {
1206
- attributes: ["id", "name"] // Only return these fields
1207
- });
1208
-
1209
- // Use pagination for large datasets
1210
- const users = await User.where({}, {
1211
- limit: 100,
1212
- skip: 0
1213
- });
449
+ For complete documentation, examples, and guides:
1214
450
 
1215
- // Prefer specific queries over scanning all records
1216
- const activeUsers = await User.where({ active: true }); // Good
1217
- const allUsers = (await User.where({})).filter(u => u.active); // Bad
1218
- ```
1219
-
1220
- ### Debugging
1221
-
1222
- ```typescript
1223
- // Enable debug logging (if available)
1224
- Dynamite.config({
1225
- region: "us-east-1",
1226
- logger: console // Log all DynamoDB operations
1227
- });
1228
-
1229
- // Log query parameters
1230
- const users = await User.where({ active: true });
1231
- console.log("Found users:", users.length);
1232
- ```
1233
-
1234
- ### Best Practices
1235
-
1236
- 1. **Always define a primary key** with `@PrimaryKey()` or `@Index()`
1237
- 2. **Use TypeScript strict mode** for better type safety
1238
- 3. **Validate user input** with `@Validate()` decorators
1239
- 4. **Use attributes selection** to limit data transfer
1240
- 5. **Handle relationships carefully** to avoid N+1 queries
1241
- 6. **Use transactions** for complex operations (if needed)
1242
- 7. **Monitor DynamoDB costs** in production
451
+ **[arcaelas.github.io/dynamite](https://arcaelas.github.io/dynamite)**
1243
452
 
1244
453
  ---
1245
454
 
1246
- ## 📄 License
455
+ ## License
1247
456
 
1248
457
  MIT License - see [LICENSE](LICENSE) file for details.
1249
458
 
1250
459
  ---
1251
460
 
1252
- ## 🤝 Contributing
1253
-
1254
- 1. Fork the repository
1255
- 2. Create a feature branch: `git checkout -b feature/amazing-feature`
1256
- 3. Make your changes and add tests
1257
- 4. Ensure tests pass: `npm test`
1258
- 5. Commit changes: `git commit -m 'feat: add amazing feature'`
1259
- 6. Push to branch: `git push origin feature/amazing-feature`
1260
- 7. Open a Pull Request
1261
-
1262
- ### Development Guidelines
1263
-
1264
- - Follow TypeScript strict mode
1265
- - Add tests for new features
1266
- - Update documentation
1267
- - Use conventional commits
1268
- - Ensure backward compatibility
1269
-
1270
- ---
1271
-
1272
- **Made with ❤️ by [Miguel Alejandro](https://github.com/arcaelas) - [Arcaelas Insiders](https://github.com/arcaelas)**
461
+ **Made with care by [Miguel Alejandro](https://github.com/arcaelas) - [Arcaelas Insiders](https://github.com/arcaelas)**