betterddb 0.6.13 → 0.7.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/.github/workflows/npm-publish.yml +33 -0
- package/.github/workflows/test.yml +42 -0
- package/API_REFERENCE.md +544 -0
- package/CONTRIBUTING.md +225 -0
- package/LICENCSE +21 -0
- package/README.md +59 -50
- package/docker-compose.yml +16 -0
- package/eslint.config.mjs +29 -0
- package/jest.config.js +16 -0
- package/package.json +15 -10
- package/prettier.config.js +6 -0
- package/src/betterddb.ts +267 -0
- package/src/builders/batch-get-builder.ts +56 -0
- package/src/builders/create-builder.ts +97 -0
- package/src/builders/delete-builder.ts +87 -0
- package/src/builders/get-builder.ts +78 -0
- package/src/builders/index.ts +7 -0
- package/src/builders/query-builder.ts +242 -0
- package/src/builders/scan-builder.ts +98 -0
- package/src/builders/update-builder.ts +344 -0
- package/src/index.ts +4 -0
- package/src/operator.ts +43 -0
- package/src/types/index.ts +1 -0
- package/src/types/paginated-result.ts +6 -0
- package/test/batch-get.test.ts +122 -0
- package/test/create.test.ts +121 -0
- package/test/delete.test.ts +93 -0
- package/test/get.test.ts +98 -0
- package/test/query.test.ts +206 -0
- package/test/scan.test.ts +130 -0
- package/test/update.test.ts +352 -0
- package/test/utils/table-setup.ts +62 -0
- package/tsconfig.json +23 -0
- package/dist/betterddb.js +0 -164
- package/dist/builders/batch-get-builder.js +0 -54
- package/dist/builders/create-builder.js +0 -84
- package/dist/builders/delete-builder.js +0 -75
- package/dist/builders/get-builder.js +0 -76
- package/dist/builders/query-builder.js +0 -164
- package/dist/builders/scan-builder.js +0 -76
- package/dist/builders/update-builder.js +0 -195
- package/dist/index.js +0 -17
- package/dist/operator.js +0 -31
- package/dist/types/betterddb.d.ts +0 -137
- package/dist/types/betterddb.d.ts.map +0 -1
- package/dist/types/builders/batch-get-builder.d.ts +0 -16
- package/dist/types/builders/batch-get-builder.d.ts.map +0 -1
- package/dist/types/builders/create-builder.d.ts +0 -12
- package/dist/types/builders/create-builder.d.ts.map +0 -1
- package/dist/types/builders/delete-builder.d.ts +0 -18
- package/dist/types/builders/delete-builder.d.ts.map +0 -1
- package/dist/types/builders/get-builder.d.ts +0 -18
- package/dist/types/builders/get-builder.d.ts.map +0 -1
- package/dist/types/builders/query-builder.d.ts +0 -29
- package/dist/types/builders/query-builder.d.ts.map +0 -1
- package/dist/types/builders/scan-builder.d.ts +0 -21
- package/dist/types/builders/scan-builder.d.ts.map +0 -1
- package/dist/types/builders/update-builder.d.ts +0 -36
- package/dist/types/builders/update-builder.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -2
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/operator.d.ts +0 -3
- package/dist/types/operator.d.ts.map +0 -1
- package/dist/types/paginated-result.js +0 -2
- package/dist/types/types/paginated-result.d.ts +0 -6
- package/dist/types/types/paginated-result.d.ts.map +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 20
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm test
|
|
20
|
+
|
|
21
|
+
publish-npm:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 20
|
|
29
|
+
registry-url: https://registry.npmjs.org/
|
|
30
|
+
- run: npm ci
|
|
31
|
+
- run: npm publish
|
|
32
|
+
env:
|
|
33
|
+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
push:
|
|
7
|
+
branches: [ master ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Docker Buildx
|
|
17
|
+
uses: docker/setup-buildx-action@v3
|
|
18
|
+
|
|
19
|
+
- name: Install Docker Compose
|
|
20
|
+
run: |
|
|
21
|
+
sudo curl -L "https://github.com/docker/compose/releases/download/v2.3.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
22
|
+
sudo chmod +x /usr/local/bin/docker-compose
|
|
23
|
+
docker-compose --version
|
|
24
|
+
continue-on-error: false
|
|
25
|
+
|
|
26
|
+
- name: Setup Node.js
|
|
27
|
+
uses: actions/setup-node@v4
|
|
28
|
+
with:
|
|
29
|
+
node-version: '20'
|
|
30
|
+
cache: 'npm'
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: npm ci
|
|
34
|
+
|
|
35
|
+
- name: Type check
|
|
36
|
+
run: npm run typecheck
|
|
37
|
+
|
|
38
|
+
- name: Lint
|
|
39
|
+
run: npm run lint
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: npm test
|
package/API_REFERENCE.md
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# BetterDDB API Reference
|
|
2
|
+
|
|
3
|
+
This document provides a comprehensive reference for the BetterDDB library, explaining its classes, methods, and usage patterns.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Initialization](#initialization)
|
|
8
|
+
- [Schema Definition](#schema-definition)
|
|
9
|
+
- [CRUD Operations](#crud-operations)
|
|
10
|
+
- [Create](#create)
|
|
11
|
+
- [Read](#read)
|
|
12
|
+
- [Update](#update)
|
|
13
|
+
- [Delete](#delete)
|
|
14
|
+
- [Query Operations](#query-operations)
|
|
15
|
+
- [Basic Queries](#basic-queries)
|
|
16
|
+
- [Using Indexes](#using-indexes)
|
|
17
|
+
- [Filtering](#filtering)
|
|
18
|
+
- [Scan Operations](#scan-operations)
|
|
19
|
+
- [Batch Operations](#batch-operations)
|
|
20
|
+
- [Transaction Operations](#transaction-operations)
|
|
21
|
+
- [Advanced Features](#advanced-features)
|
|
22
|
+
- [Automatic Timestamps](#automatic-timestamps)
|
|
23
|
+
- [Versioning](#versioning)
|
|
24
|
+
|
|
25
|
+
## Initialization
|
|
26
|
+
|
|
27
|
+
### BetterDDB Constructor
|
|
28
|
+
|
|
29
|
+
The main entry point for interacting with DynamoDB.
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
constructor(options: BetterDDBOptions<T>)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### Parameters
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
export interface BetterDDBOptions<T> {
|
|
39
|
+
schema: z.AnyZodObject;
|
|
40
|
+
tableName: string;
|
|
41
|
+
entityType?: string;
|
|
42
|
+
keys: KeysConfig<T>;
|
|
43
|
+
client: DynamoDBDocumentClient;
|
|
44
|
+
counter?: boolean;
|
|
45
|
+
timestamps?: boolean;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- **schema**: Zod schema for validation
|
|
50
|
+
- **tableName**: Your DynamoDB table name
|
|
51
|
+
- **entityType**: Optional type discriminator for multi-entity tables
|
|
52
|
+
- **keys**: Configuration for primary key, sort key, and GSIs
|
|
53
|
+
- **client**: AWS SDK v3 DynamoDB document client
|
|
54
|
+
- **counter**: Whether to use a counter field for optimistic locking
|
|
55
|
+
- **timestamps**: Whether to automatically manage `createdAt` and `updatedAt` timestamps
|
|
56
|
+
|
|
57
|
+
#### Example
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { BetterDDB } from "betterddb";
|
|
61
|
+
import { z } from "zod";
|
|
62
|
+
import { DynamoDBDocumentClient, DynamoDB } from "@aws-sdk/lib-dynamodb";
|
|
63
|
+
|
|
64
|
+
// Define schema
|
|
65
|
+
const UserSchema = z.object({
|
|
66
|
+
id: z.string(),
|
|
67
|
+
name: z.string(),
|
|
68
|
+
email: z.string().email(),
|
|
69
|
+
createdAt: z.string().optional(),
|
|
70
|
+
updatedAt: z.string().optional(),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Init client
|
|
74
|
+
const client = DynamoDBDocumentClient.from(
|
|
75
|
+
new DynamoDB({
|
|
76
|
+
region: "us-east-1",
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Initialize BetterDDB
|
|
81
|
+
const userDdb = new BetterDDB({
|
|
82
|
+
schema: UserSchema,
|
|
83
|
+
tableName: "Users",
|
|
84
|
+
keys: {
|
|
85
|
+
primary: {
|
|
86
|
+
name: "pk",
|
|
87
|
+
definition: { build: (raw) => `USER#${raw.id}` },
|
|
88
|
+
},
|
|
89
|
+
sort: {
|
|
90
|
+
name: "sk",
|
|
91
|
+
definition: { build: (raw) => `PROFILE` },
|
|
92
|
+
},
|
|
93
|
+
gsis: {
|
|
94
|
+
EmailIndex: {
|
|
95
|
+
name: "EmailIndex",
|
|
96
|
+
primary: {
|
|
97
|
+
name: "gsi1pk",
|
|
98
|
+
definition: "email",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
client,
|
|
104
|
+
timestamps: true,
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Schema Definition
|
|
109
|
+
|
|
110
|
+
BetterDDB uses [Zod](https://github.com/colinhacks/zod) for schema validation and type inference. Your schema defines both runtime validation rules and TypeScript types.
|
|
111
|
+
|
|
112
|
+
### Basic Schema
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const Schema = z.object({
|
|
116
|
+
id: z.string(),
|
|
117
|
+
name: z.string(),
|
|
118
|
+
email: z.string().email(),
|
|
119
|
+
age: z.number().optional(),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
type Entity = z.infer<typeof Schema>; // TypeScript type inference
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Schema with Computed Fields
|
|
126
|
+
|
|
127
|
+
To allow for computed fields (like keys), use `.passthrough()`:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const Schema = z
|
|
131
|
+
.object({
|
|
132
|
+
id: z.string(),
|
|
133
|
+
name: z.string(),
|
|
134
|
+
})
|
|
135
|
+
.passthrough();
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## CRUD Operations
|
|
139
|
+
|
|
140
|
+
### Create
|
|
141
|
+
|
|
142
|
+
Creates a new item in the DynamoDB table with automatic validation and key computation.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
create(item: T): CreateBuilder<T>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### CreateBuilder Methods
|
|
149
|
+
|
|
150
|
+
- **execute()**: Performs the create operation and returns the created item
|
|
151
|
+
- **withCondition(condition: string, expressionAttrs?: Record<string, any>)**: Adds a condition expression
|
|
152
|
+
|
|
153
|
+
#### Example
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// Simple create
|
|
157
|
+
const user = await userDdb
|
|
158
|
+
.create({
|
|
159
|
+
id: "123",
|
|
160
|
+
name: "Alice",
|
|
161
|
+
email: "alice@example.com",
|
|
162
|
+
})
|
|
163
|
+
.execute();
|
|
164
|
+
|
|
165
|
+
// Create with condition
|
|
166
|
+
const user = await userDdb
|
|
167
|
+
.create({
|
|
168
|
+
id: "123",
|
|
169
|
+
name: "Alice",
|
|
170
|
+
email: "alice@example.com",
|
|
171
|
+
})
|
|
172
|
+
.withCondition("attribute_not_exists(#pk)", { "#pk": "pk" })
|
|
173
|
+
.execute();
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Read
|
|
177
|
+
|
|
178
|
+
Retrieves items from the DynamoDB table.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
get(key: Partial<T>): GetBuilder<T>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### GetBuilder Methods
|
|
185
|
+
|
|
186
|
+
- **execute()**: Performs the get operation and returns the item
|
|
187
|
+
- **withProjection(attributes: string[])**: Specifies which attributes to return
|
|
188
|
+
- **withConsistentRead(consistent: boolean)**: Uses consistent read
|
|
189
|
+
|
|
190
|
+
#### Example
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// Simple get
|
|
194
|
+
const user = await userDdb.get({ id: "123" }).execute();
|
|
195
|
+
|
|
196
|
+
// Get with projection
|
|
197
|
+
const userNameOnly = await userDdb
|
|
198
|
+
.get({ id: "123" })
|
|
199
|
+
.withProjection(["name", "email"])
|
|
200
|
+
.execute();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Batch Get
|
|
204
|
+
|
|
205
|
+
Retrieves multiple items in a single operation.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
batchGet(keys: Partial<T>[]): BatchGetBuilder<T>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Example
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const users = await userDdb.batchGet([{ id: "123" }, { id: "456" }]).execute();
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Update
|
|
218
|
+
|
|
219
|
+
Updates an existing item with automatic validation.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
update(key: Partial<T>, expectedVersion?: number): UpdateBuilder<T>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### UpdateBuilder Methods
|
|
226
|
+
|
|
227
|
+
- **set(attributes: Partial<T>)**: Sets attributes to specific values
|
|
228
|
+
- **remove(attributes: string[])**: Removes attributes
|
|
229
|
+
- **add(attributes: Record<string, number>)**: Adds numeric values to attributes
|
|
230
|
+
- **delete(attributes: Record<string, any[]>)**: Removes elements from sets
|
|
231
|
+
- **withCondition(condition: string, expressionAttrs?: Record<string, any>)**: Adds a condition expression
|
|
232
|
+
- **execute()**: Performs the update operation and returns the updated item
|
|
233
|
+
- **toTransactUpdate()**: Converts the update to a transaction operation
|
|
234
|
+
|
|
235
|
+
#### Example
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// Simple update
|
|
239
|
+
const user = await userDdb
|
|
240
|
+
.update({ id: "123" })
|
|
241
|
+
.set({ name: "Alice Smith" })
|
|
242
|
+
.execute();
|
|
243
|
+
|
|
244
|
+
// Complex update
|
|
245
|
+
const user = await userDdb
|
|
246
|
+
.update({ id: "123" }, 1) // Version check
|
|
247
|
+
.set({ name: "Alice Smith" })
|
|
248
|
+
.remove(["oldField"])
|
|
249
|
+
.add({ age: 1 })
|
|
250
|
+
.withCondition("attribute_exists(#id)", { "#id": "id" })
|
|
251
|
+
.execute();
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Delete
|
|
255
|
+
|
|
256
|
+
Deletes an item from the DynamoDB table.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
delete(key: Partial<T>): DeleteBuilder<T>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### DeleteBuilder Methods
|
|
263
|
+
|
|
264
|
+
- **execute()**: Performs the delete operation
|
|
265
|
+
- **withCondition(condition: string, expressionAttrs?: Record<string, any>)**: Adds a condition expression
|
|
266
|
+
- **toTransactDelete()**: Converts the delete to a transaction operation
|
|
267
|
+
|
|
268
|
+
#### Example
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
// Simple delete
|
|
272
|
+
await userDdb.delete({ id: "123" }).execute();
|
|
273
|
+
|
|
274
|
+
// Delete with condition
|
|
275
|
+
await userDdb
|
|
276
|
+
.delete({ id: "123" })
|
|
277
|
+
.withCondition("#status = :status", {
|
|
278
|
+
"#status": "status",
|
|
279
|
+
":status": "inactive",
|
|
280
|
+
})
|
|
281
|
+
.execute();
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Query Operations
|
|
285
|
+
|
|
286
|
+
Performs a DynamoDB query operation with a fluent interface.
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
query(keyCondition: Partial<T>): QueryBuilder<T>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### QueryBuilder Methods
|
|
293
|
+
|
|
294
|
+
- **execute()**: Performs the query operation and returns the results
|
|
295
|
+
- **where(operator: string, value: any)**: Adds a condition on the sort key
|
|
296
|
+
- **filter(attribute: keyof T, operator: string, value: any)**: Adds a filter condition
|
|
297
|
+
- **usingIndex(indexName: string)**: Specifies a GSI to query
|
|
298
|
+
- **limitResults(limit: number)**: Limits the number of results
|
|
299
|
+
- **scanDescending()**: Changes the scan direction to descending
|
|
300
|
+
- **startFrom(lastEvaluatedKey: Record<string, any>)**: Enables pagination
|
|
301
|
+
|
|
302
|
+
### Basic Queries
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Query by primary key
|
|
306
|
+
const results = await userDdb.query({ id: "123" }).execute();
|
|
307
|
+
|
|
308
|
+
// Query with sort key condition
|
|
309
|
+
const results = await userDdb
|
|
310
|
+
.query({ id: "123" })
|
|
311
|
+
.where("begins_with", { email: "a" })
|
|
312
|
+
.execute();
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Using Indexes
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// Query using GSI
|
|
319
|
+
const results = await userDdb
|
|
320
|
+
.query({ email: "alice@example.com" })
|
|
321
|
+
.usingIndex("EmailIndex")
|
|
322
|
+
.execute();
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Filtering
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// Query with filter
|
|
329
|
+
const results = await userDdb
|
|
330
|
+
.query({ id: "123" })
|
|
331
|
+
.filter("age", ">", 21)
|
|
332
|
+
.limitResults(10)
|
|
333
|
+
.execute();
|
|
334
|
+
|
|
335
|
+
// Multiple filters
|
|
336
|
+
const results = await userDdb
|
|
337
|
+
.query({ id: "123" })
|
|
338
|
+
.filter("age", ">", 21)
|
|
339
|
+
.filter("name", "begins_with", "A")
|
|
340
|
+
.execute();
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Pagination
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
const firstPage = await userDdb.query({ id: "123" }).limitResults(10).execute();
|
|
347
|
+
|
|
348
|
+
if (firstPage.lastEvaluatedKey) {
|
|
349
|
+
const secondPage = await userDdb
|
|
350
|
+
.query({ id: "123" })
|
|
351
|
+
.limitResults(10)
|
|
352
|
+
.startFrom(firstPage.lastEvaluatedKey)
|
|
353
|
+
.execute();
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Scan Operations
|
|
358
|
+
|
|
359
|
+
Performs a DynamoDB scan operation.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
scan(): ScanBuilder<T>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### ScanBuilder Methods
|
|
366
|
+
|
|
367
|
+
- **execute()**: Performs the scan operation and returns the results
|
|
368
|
+
- **filter(attribute: keyof T, operator: string, value: any)**: Adds a filter condition
|
|
369
|
+
- **limitResults(limit: number)**: Limits the number of results
|
|
370
|
+
- **startFrom(lastEvaluatedKey: Record<string, any>)**: Enables pagination
|
|
371
|
+
- **usingIndex(indexName: string)**: Specifies a GSI to scan
|
|
372
|
+
|
|
373
|
+
### Example
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// Simple scan
|
|
377
|
+
const results = await userDdb.scan().execute();
|
|
378
|
+
|
|
379
|
+
// Scan with filter
|
|
380
|
+
const results = await userDdb
|
|
381
|
+
.scan()
|
|
382
|
+
.filter("status", "==", "active")
|
|
383
|
+
.limitResults(100)
|
|
384
|
+
.execute();
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Batch Operations
|
|
388
|
+
|
|
389
|
+
Performs multiple write operations in a single request.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
batchWrite(operations: {
|
|
393
|
+
puts?: T[];
|
|
394
|
+
deletes?: Partial<T>[]
|
|
395
|
+
}): Promise<void>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Example
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
await userDdb.batchWrite({
|
|
402
|
+
puts: [
|
|
403
|
+
{ id: "123", name: "Alice", email: "alice@example.com" },
|
|
404
|
+
{ id: "456", name: "Bob", email: "bob@example.com" },
|
|
405
|
+
],
|
|
406
|
+
deletes: [{ id: "789" }],
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## Transaction Operations
|
|
411
|
+
|
|
412
|
+
Performs multiple operations atomically.
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
transactWrite(transactItems: any[]): Promise<void>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Example
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
// Build transaction items
|
|
422
|
+
const createItem = userDdb
|
|
423
|
+
.create({
|
|
424
|
+
id: "123",
|
|
425
|
+
name: "Alice",
|
|
426
|
+
email: "alice@example.com",
|
|
427
|
+
})
|
|
428
|
+
.toTransactPut();
|
|
429
|
+
|
|
430
|
+
const updateItem = userDdb
|
|
431
|
+
.update({ id: "456" })
|
|
432
|
+
.set({ name: "Bob Smith" })
|
|
433
|
+
.toTransactUpdate();
|
|
434
|
+
|
|
435
|
+
const deleteItem = userDdb.delete({ id: "789" }).toTransactDelete();
|
|
436
|
+
|
|
437
|
+
// Execute transaction
|
|
438
|
+
await userDdb.transactWrite([createItem, updateItem, deleteItem]);
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## Advanced Features
|
|
442
|
+
|
|
443
|
+
### Automatic Timestamps
|
|
444
|
+
|
|
445
|
+
When enabled, BetterDDB automatically manages:
|
|
446
|
+
|
|
447
|
+
- **createdAt**: Set on item creation
|
|
448
|
+
- **updatedAt**: Set on item creation and updated on every update
|
|
449
|
+
|
|
450
|
+
Enable with:
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
const ddb = new BetterDDB({
|
|
454
|
+
// ...other options
|
|
455
|
+
timestamps: true,
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Versioning
|
|
460
|
+
|
|
461
|
+
Enables optimistic locking with version numbers.
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
// Update with version checking
|
|
465
|
+
const user = await userDdb
|
|
466
|
+
.update({ id: "123" }, 1) // Expected version
|
|
467
|
+
.set({ name: "Alice Smith" })
|
|
468
|
+
.execute();
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
This will only update if the current version is 1, then increment it to 2.
|
|
472
|
+
|
|
473
|
+
### Counter
|
|
474
|
+
|
|
475
|
+
Enables automatic counter incrementing for items.
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
const ddb = new BetterDDB({
|
|
479
|
+
// ...other options
|
|
480
|
+
counter: true,
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## Key Management
|
|
485
|
+
|
|
486
|
+
### Key Definitions
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
export interface KeysConfig<T> {
|
|
490
|
+
primary: PrimaryKeyConfig<T>;
|
|
491
|
+
sort?: SortKeyConfig<T>;
|
|
492
|
+
gsis?: Record<string, GSIConfig<T>>;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export interface PrimaryKeyConfig<T> {
|
|
496
|
+
name: string;
|
|
497
|
+
definition: KeyDefinition<T>;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export interface SortKeyConfig<T> {
|
|
501
|
+
name: string;
|
|
502
|
+
definition: KeyDefinition<T>;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
export interface GSIConfig<T> {
|
|
506
|
+
name: string;
|
|
507
|
+
primary: PrimaryKeyConfig<T>;
|
|
508
|
+
sort?: SortKeyConfig<T>;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export type KeyDefinition<T> =
|
|
512
|
+
| keyof T
|
|
513
|
+
| {
|
|
514
|
+
build: (rawKey: Partial<T>) => string;
|
|
515
|
+
};
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Using Raw Attribute Names
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
const ddb = new BetterDDB<User>({
|
|
522
|
+
keys: {
|
|
523
|
+
primary: {
|
|
524
|
+
name: "pk",
|
|
525
|
+
definition: "id", // Use the raw 'id' attribute
|
|
526
|
+
},
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Using Computed Keys
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
const ddb = new BetterDDB<User>({
|
|
535
|
+
keys: {
|
|
536
|
+
primary: {
|
|
537
|
+
name: "pk",
|
|
538
|
+
definition: {
|
|
539
|
+
build: (raw) => `USER#${raw.id}`,
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
});
|
|
544
|
+
```
|