betterddb 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +33 -0
- package/README.md +152 -46
- package/lib/betterddb.d.ts +33 -98
- package/lib/betterddb.js +12 -72
- package/lib/builders/create-builder.d.ts +7 -6
- package/lib/builders/delete-builder.d.ts +3 -2
- package/lib/builders/get-builder.d.ts +8 -7
- package/lib/builders/query-builder.d.ts +8 -7
- package/lib/builders/query-builder.js +1 -1
- package/lib/builders/scan-builder.d.ts +8 -7
- package/lib/builders/scan-builder.js +1 -1
- package/lib/builders/update-builder.d.ts +8 -7
- package/package.json +1 -1
- package/src/betterddb.ts +73 -160
- package/src/builders/create-builder.ts +9 -8
- package/src/builders/delete-builder.ts +3 -2
- package/src/builders/get-builder.ts +9 -8
- package/src/builders/query-builder.ts +10 -9
- package/src/builders/scan-builder.ts +10 -9
- package/src/builders/update-builder.ts +14 -13
- package/test/create.test.ts +73 -0
- package/test/delete.test.ts +58 -0
- package/test/get.test.ts +58 -0
- package/test/query.test.ts +73 -0
- package/test/scan.test.ts +66 -0
- package/test/update.test.ts +58 -0
- package/test/utils/table-setup.ts +55 -0
- package/test/placeholder.test.ts +0 -99
|
@@ -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}}
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# betterddb
|
|
1
|
+
# betterddb [IN DEVELOPMENT - NOT READY FOR PRODUCTION - BREAKING CHANGES - PLEASE FOR THE LOVE OF GOD DO NOT USE]
|
|
2
2
|
|
|
3
|
-
**betterddb** is a definition-based DynamoDB wrapper library written in TypeScript. It provides a generic, schema-driven Data Access Layer (DAL) using [Zod](https://github.com/colinhacks/zod) for runtime validation and the AWS SDK for DynamoDB operations. With built-in support for compound keys, computed indexes, automatic timestamp injection, transactional and batch operations, and
|
|
3
|
+
**betterddb** is a definition-based DynamoDB wrapper library written in TypeScript. It provides a generic, schema-driven Data Access Layer (DAL) using [Zod](https://github.com/colinhacks/zod) for runtime validation and the AWS SDK for DynamoDB operations. With built-in support for compound keys, computed indexes, automatic timestamp injection, transactional and batch operations, and a fluent builder API for all CRUD operations (create, get, update, delete) as well as queries and scans, **betterddb** lets you work with DynamoDB using definitions instead of ad hoc query code.
|
|
4
|
+
|
|
5
|
+
---
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -8,15 +10,18 @@
|
|
|
8
10
|
npm install betterddb
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
---
|
|
14
|
+
|
|
11
15
|
## Usage Example
|
|
12
|
-
|
|
16
|
+
|
|
17
|
+
Below is an example of using **betterddb** for a User entity with a compound key, and using the new fluent builder APIs for create, get, update, and delete, as well as for query and scan operations.
|
|
13
18
|
|
|
14
19
|
```ts
|
|
15
20
|
import { BetterDDB } from 'betterddb';
|
|
16
21
|
import { z } from 'zod';
|
|
17
22
|
import { DynamoDB } from 'aws-sdk';
|
|
18
23
|
|
|
19
|
-
// Define the User schema. Use .passthrough()
|
|
24
|
+
// Define the User schema. Use .passthrough() to allow computed keys.
|
|
20
25
|
const UserSchema = z.object({
|
|
21
26
|
tenantId: z.string(),
|
|
22
27
|
userId: z.string(),
|
|
@@ -25,9 +30,9 @@ const UserSchema = z.object({
|
|
|
25
30
|
createdAt: z.string(),
|
|
26
31
|
updatedAt: z.string(),
|
|
27
32
|
version: z.number().optional()
|
|
28
|
-
});
|
|
33
|
+
}).passthrough();
|
|
29
34
|
|
|
30
|
-
// Configure the DynamoDB DocumentClient (
|
|
35
|
+
// Configure the DynamoDB DocumentClient (example using LocalStack)
|
|
31
36
|
const client = new DynamoDB.DocumentClient({
|
|
32
37
|
region: 'us-east-1',
|
|
33
38
|
endpoint: 'http://localhost:4566'
|
|
@@ -40,72 +45,173 @@ const userDdb = new BetterDDB({
|
|
|
40
45
|
keys: {
|
|
41
46
|
primary: {
|
|
42
47
|
name: 'pk',
|
|
43
|
-
// Compute the partition key from tenantId
|
|
48
|
+
// Compute the partition key from tenantId.
|
|
44
49
|
definition: { build: (raw) => `TENANT#${raw.tenantId}` }
|
|
45
50
|
},
|
|
46
51
|
sort: {
|
|
47
52
|
name: 'sk',
|
|
48
|
-
// Compute the sort key from userId
|
|
53
|
+
// Compute the sort key from userId.
|
|
49
54
|
definition: { build: (raw) => `USER#${raw.userId}` }
|
|
50
55
|
},
|
|
51
56
|
gsis: {
|
|
52
57
|
// Example: a Global Secondary Index on email.
|
|
53
58
|
EmailIndex: {
|
|
54
|
-
primary: {
|
|
55
|
-
name: 'email',
|
|
56
|
-
definition: 'email'
|
|
57
|
-
}
|
|
59
|
+
primary: { name: 'email', definition: 'email' }
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
},
|
|
61
63
|
client,
|
|
62
|
-
autoTimestamps: true
|
|
64
|
+
autoTimestamps: true,
|
|
65
|
+
entityName: 'USER'
|
|
63
66
|
});
|
|
64
67
|
|
|
65
|
-
// Use the BetterDDB instance to create and query items.
|
|
66
68
|
(async () => {
|
|
67
|
-
// Create
|
|
68
|
-
|
|
69
|
+
// ### Create Operation ###
|
|
70
|
+
// Use the CreateBuilder to build and execute a create operation.
|
|
71
|
+
const newUser = await userDdb.createBuilder({
|
|
69
72
|
tenantId: 'tenant1',
|
|
70
73
|
userId: 'user123',
|
|
71
74
|
email: 'user@example.com',
|
|
72
75
|
name: 'Alice'
|
|
73
|
-
});
|
|
76
|
+
}).execute();
|
|
74
77
|
console.log('Created User:', newUser);
|
|
75
78
|
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
79
|
+
// ### Get Operation ###
|
|
80
|
+
// Use the GetBuilder to retrieve an item. Optionally, use a projection.
|
|
81
|
+
const user = await userDdb.getBuilder({ id: 'user123' })
|
|
82
|
+
.withProjection(['name', 'email'])
|
|
83
|
+
.execute();
|
|
84
|
+
console.log('Retrieved User:', user);
|
|
85
|
+
|
|
86
|
+
// ### Update Operation ###
|
|
87
|
+
// Use the UpdateBuilder to perform a fluent update.
|
|
88
|
+
const updatedUser = await userDdb.update({ tenantId: 'tenant1', userId: 'user123' }, 1)
|
|
89
|
+
.set({ name: 'Jane Doe' })
|
|
90
|
+
.remove(['obsoleteAttribute'])
|
|
91
|
+
.execute();
|
|
92
|
+
console.log('Updated User (immediate):', updatedUser);
|
|
93
|
+
|
|
94
|
+
// Or build a transaction update item and include it in a transaction:
|
|
95
|
+
const transactionUpdateItem = userDdb.update({ tenantId: 'tenant1', userId: 'user123' }, 1)
|
|
96
|
+
.set({ name: 'Jane Doe' })
|
|
97
|
+
.remove(['obsoleteAttribute'])
|
|
98
|
+
.toTransactUpdate();
|
|
99
|
+
// Assume transactWrite is available on BetterDDB for executing a transaction.
|
|
100
|
+
await userDdb.transactWrite([transactionUpdateItem]);
|
|
101
|
+
console.log('Updated User (transaction) executed.');
|
|
102
|
+
|
|
103
|
+
// ### Delete Operation ###
|
|
104
|
+
// Use the DeleteBuilder to delete an item with an optional condition.
|
|
105
|
+
await userDdb.deleteBuilder({ id: 'user123' })
|
|
106
|
+
.withCondition('#status = :expected', { ':expected': 'inactive' })
|
|
107
|
+
.execute();
|
|
108
|
+
console.log('User deleted');
|
|
109
|
+
|
|
110
|
+
// ### Query Operation ###
|
|
111
|
+
// Use the fluent QueryBuilder to query items.
|
|
112
|
+
const queryResults = await userDdb.query({ tenantId: 'tenant1' })
|
|
113
|
+
.where('name', 'begins_with', 'John')
|
|
114
|
+
.limitResults(10);
|
|
115
|
+
console.log('Query Results:', queryResults);
|
|
116
|
+
|
|
117
|
+
// ### Scan Operation ###
|
|
118
|
+
// Use the fluent ScanBuilder to scan the table with a filter.
|
|
119
|
+
const scanResults = await userDdb.scan()
|
|
120
|
+
.where('tenantId', 'eq', 'tenant1')
|
|
121
|
+
.limitResults(50);
|
|
122
|
+
console.log('Scan Results:', scanResults);
|
|
86
123
|
})();
|
|
87
124
|
```
|
|
88
125
|
|
|
89
|
-
|
|
90
|
-
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## API Overview
|
|
129
|
+
|
|
130
|
+
**betterddb** exposes a generic class `BetterDDB<T>` with the following methods:
|
|
131
|
+
|
|
132
|
+
### Fluent CRUD Builders
|
|
133
|
+
|
|
134
|
+
- **CreateBuilder**
|
|
135
|
+
- `createBuilder(item: T): CreateBuilder<T>`
|
|
136
|
+
- Builds a put request with automatic timestamp and key computation.
|
|
137
|
+
- Usage:
|
|
138
|
+
```ts
|
|
139
|
+
await betterDdb.createBuilder(item).execute();
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- **GetBuilder**
|
|
143
|
+
- `getBuilder(key: Partial<T>): GetBuilder<T>`
|
|
144
|
+
- Builds a get request. Supports projections via `.withProjection()`.
|
|
145
|
+
- Usage:
|
|
146
|
+
```ts
|
|
147
|
+
const result = await betterDdb.getBuilder({ id: 'user123' })
|
|
148
|
+
.withProjection(['name', 'email'])
|
|
149
|
+
.execute();
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- **DeleteBuilder**
|
|
153
|
+
- `deleteBuilder(key: Partial<T>): DeleteBuilder<T>`
|
|
154
|
+
- Builds a delete request. Supports condition expressions via `.withCondition()`.
|
|
155
|
+
- Usage:
|
|
156
|
+
```ts
|
|
157
|
+
await betterDdb.deleteBuilder({ id: 'user123' })
|
|
158
|
+
.withCondition('#status = :expected', { ':expected': 'inactive' })
|
|
159
|
+
.execute();
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Fluent Update Builder
|
|
163
|
+
|
|
164
|
+
- `update(key: Partial<T>, expectedVersion?: number): UpdateBuilder<T>`
|
|
165
|
+
- Provides chainable methods such as `.set()`, `.remove()`, `.add()`, and `.delete()`.
|
|
166
|
+
- Also supports transaction mode:
|
|
167
|
+
- `.toTransactUpdate()` returns a transaction item.
|
|
168
|
+
- `.transactWrite([...])` allows you to combine update items in a transaction.
|
|
169
|
+
- Usage:
|
|
170
|
+
```ts
|
|
171
|
+
await betterDdb.update({ id: 'user123' }, 1)
|
|
172
|
+
.set({ name: 'Jane Doe' })
|
|
173
|
+
.remove(['obsoleteAttribute'])
|
|
174
|
+
.execute();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Fluent Query & Scan Builders
|
|
178
|
+
|
|
179
|
+
- **QueryBuilder**
|
|
180
|
+
- `query(key: Partial<T>): QueryBuilder<T>`
|
|
181
|
+
- Allows you to chain conditions (via `.where()`), sort direction, limits, and pagination.
|
|
182
|
+
- Usage:
|
|
183
|
+
```ts
|
|
184
|
+
const results = await betterDdb.query({ tenantId: 'tenant1' })
|
|
185
|
+
.where('name', 'begins_with', 'John')
|
|
186
|
+
.limitResults(10);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- **ScanBuilder**
|
|
190
|
+
- `scan(): ScanBuilder<T>`
|
|
191
|
+
- Provides a fluent API to filter and paginate scan operations.
|
|
192
|
+
- Usage:
|
|
193
|
+
```ts
|
|
194
|
+
const results = await betterDdb.scan()
|
|
195
|
+
.where('tenantId', 'eq', 'tenant1')
|
|
196
|
+
.limitResults(50);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Batch and Transaction Operations
|
|
200
|
+
|
|
201
|
+
- **Batch Operations:**
|
|
202
|
+
- `batchWrite(ops: { puts?: T[]; deletes?: Partial<T>[] }): Promise<void>`
|
|
203
|
+
- `batchGet(rawKeys: Partial<T>[]): Promise<T[]>`
|
|
204
|
+
|
|
205
|
+
- **Transaction Helpers:**
|
|
206
|
+
- `buildTransactPut(item: T)`
|
|
207
|
+
- `buildTransactUpdate(rawKey: Partial<T>, update: Partial<T>, options?: { expectedVersion?: number })`
|
|
208
|
+
- `buildTransactDelete(rawKey: Partial<T>)`
|
|
209
|
+
- `transactWrite(...)` and `transactGetByKeys(...)`
|
|
91
210
|
|
|
92
|
-
```ts
|
|
93
|
-
create(item: T): Promise<T>
|
|
94
|
-
get(rawKey: Partial<T>): Promise<T | null>
|
|
95
|
-
update(rawKey: Partial<T>, update: Partial<T>, options?: { expectedVersion?: number }): Promise<T>
|
|
96
|
-
delete(rawKey: Partial<T>): Promise<void>
|
|
97
|
-
queryByGsi(gsiName: string, key: Partial<T>, sortKeyCondition?: { operator: "eq" | "begins_with" | "between"; values: any | [any, any] }): Promise<T[]>
|
|
98
|
-
queryByPrimaryKey(rawKey: Partial<T>, sortKeyCondition?: { operator: "eq" | "begins_with" | "between"; values: any | [any, any] }, options?: { limit?: number; lastKey?: Record<string, any> }): Promise<{ items: T[]; lastKey?: Record<string, any> }>
|
|
99
|
-
Batch operations:
|
|
100
|
-
batchWrite(ops: { puts?: T[]; deletes?: Partial<T>[] }): Promise<void>
|
|
101
|
-
batchGet(rawKeys: Partial<T>[]): Promise<T[]>
|
|
102
|
-
Transaction helper methods:
|
|
103
|
-
buildTransactPut(item: T)
|
|
104
|
-
buildTransactUpdate(rawKey: Partial<T>, update: Partial<T>, options?: { expectedVersion?: number })
|
|
105
|
-
buildTransactDelete(rawKey: Partial<T>)
|
|
106
|
-
transactWrite(...) and transactGetByKeys(...)
|
|
107
211
|
For complete details, please refer to the API documentation.
|
|
108
|
-
```
|
|
109
212
|
|
|
110
|
-
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
111
217
|
MIT
|
package/lib/betterddb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
import { DynamoDB } from 'aws-sdk';
|
|
3
3
|
import { QueryBuilder } from './builders/query-builder';
|
|
4
4
|
import { ScanBuilder } from './builders/scan-builder';
|
|
@@ -6,124 +6,59 @@ import { UpdateBuilder } from './builders/update-builder';
|
|
|
6
6
|
import { CreateBuilder } from './builders/create-builder';
|
|
7
7
|
import { GetBuilder } from './builders/get-builder';
|
|
8
8
|
import { DeleteBuilder } from './builders/delete-builder';
|
|
9
|
+
export type SchemaType<S extends z.ZodType<any>> = z.infer<S>;
|
|
9
10
|
export type PrimaryKeyValue = string | number;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* or an object containing a build function that computes the value.
|
|
13
|
-
* (In this design, the attribute name is provided separately.)
|
|
14
|
-
*/
|
|
15
|
-
export type KeyDefinition<T> = keyof T | {
|
|
16
|
-
build: (rawKey: Partial<T>) => string;
|
|
11
|
+
export type KeyDefinition<S extends z.ZodType<any>> = keyof SchemaType<S> | {
|
|
12
|
+
build: (rawKey: Partial<SchemaType<S>>) => string;
|
|
17
13
|
};
|
|
18
|
-
|
|
19
|
-
* Configuration for a primary (partition) key.
|
|
20
|
-
*/
|
|
21
|
-
export interface PrimaryKeyConfig<T> {
|
|
22
|
-
/** The attribute name for the primary key in DynamoDB */
|
|
14
|
+
export interface PrimaryKeyConfig<S extends z.ZodType<any>> {
|
|
23
15
|
name: string;
|
|
24
|
-
|
|
25
|
-
* if an object, the build function is used.
|
|
26
|
-
*/
|
|
27
|
-
definition: KeyDefinition<T>;
|
|
16
|
+
definition: KeyDefinition<S>;
|
|
28
17
|
}
|
|
29
|
-
|
|
30
|
-
* Configuration for a sort key.
|
|
31
|
-
*/
|
|
32
|
-
export interface SortKeyConfig<T> {
|
|
33
|
-
/** The attribute name for the sort key in DynamoDB */
|
|
18
|
+
export interface SortKeyConfig<S extends z.ZodType<any>> {
|
|
34
19
|
name: string;
|
|
35
|
-
|
|
36
|
-
definition: KeyDefinition<T>;
|
|
20
|
+
definition: KeyDefinition<S>;
|
|
37
21
|
}
|
|
38
|
-
|
|
39
|
-
* Configuration for a Global Secondary Index (GSI).
|
|
40
|
-
*/
|
|
41
|
-
export interface GSIConfig<T> {
|
|
42
|
-
/** The name of the GSI in DynamoDB */
|
|
22
|
+
export interface GSIConfig<S extends z.ZodType<any>> {
|
|
43
23
|
name: string;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/** The sort key configuration for the GSI, if any */
|
|
47
|
-
sort?: SortKeyConfig<T>;
|
|
24
|
+
primary: PrimaryKeyConfig<S>;
|
|
25
|
+
sort?: SortKeyConfig<S>;
|
|
48
26
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export interface KeysConfig<T> {
|
|
53
|
-
primary: PrimaryKeyConfig<T>;
|
|
54
|
-
sort?: SortKeyConfig<T>;
|
|
27
|
+
export interface KeysConfig<S extends z.ZodType<any>> {
|
|
28
|
+
primary: PrimaryKeyConfig<S>;
|
|
29
|
+
sort?: SortKeyConfig<S>;
|
|
55
30
|
gsis?: {
|
|
56
|
-
[gsiName: string]: GSIConfig<
|
|
31
|
+
[gsiName: string]: GSIConfig<S>;
|
|
57
32
|
};
|
|
58
33
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*/
|
|
62
|
-
export interface BetterDDBOptions<T> {
|
|
63
|
-
schema: ZodSchema<T>;
|
|
34
|
+
export interface BetterDDBOptions<S extends z.ZodType<any>> {
|
|
35
|
+
schema: S;
|
|
64
36
|
tableName: string;
|
|
65
37
|
entityName: string;
|
|
66
|
-
keys: KeysConfig<
|
|
38
|
+
keys: KeysConfig<S>;
|
|
67
39
|
client: DynamoDB.DocumentClient;
|
|
68
|
-
/**
|
|
69
|
-
* If true, automatically inject timestamp fields:
|
|
70
|
-
* - On create, sets both `createdAt` and `updatedAt`
|
|
71
|
-
* - On update, sets `updatedAt`
|
|
72
|
-
*
|
|
73
|
-
* (T should include these fields if enabled.)
|
|
74
|
-
*/
|
|
75
40
|
autoTimestamps?: boolean;
|
|
76
41
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
*/
|
|
80
|
-
export declare class BetterDDB<T> {
|
|
81
|
-
protected schema: ZodSchema<T>;
|
|
42
|
+
export declare class BetterDDB<S extends z.ZodType<any>> {
|
|
43
|
+
protected schema: S;
|
|
82
44
|
protected tableName: string;
|
|
83
45
|
protected entityName: string;
|
|
84
46
|
protected client: DynamoDB.DocumentClient;
|
|
85
|
-
protected keys: KeysConfig<
|
|
47
|
+
protected keys: KeysConfig<S>;
|
|
86
48
|
protected autoTimestamps: boolean;
|
|
87
|
-
constructor(options: BetterDDBOptions<
|
|
88
|
-
getKeys(): KeysConfig<
|
|
49
|
+
constructor(options: BetterDDBOptions<S>);
|
|
50
|
+
getKeys(): KeysConfig<S>;
|
|
89
51
|
getTableName(): string;
|
|
90
52
|
getClient(): DynamoDB.DocumentClient;
|
|
91
|
-
getSchema():
|
|
53
|
+
getSchema(): S;
|
|
92
54
|
getAutoTimestamps(): boolean;
|
|
93
|
-
protected getKeyValue(def: KeyDefinition<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Create an item:
|
|
104
|
-
* - Computes primary key and index attributes,
|
|
105
|
-
* - Optionally injects timestamps,
|
|
106
|
-
* - Validates the item and writes it to DynamoDB.
|
|
107
|
-
*/
|
|
108
|
-
create(item: T): CreateBuilder<T>;
|
|
109
|
-
/**
|
|
110
|
-
* Get an item by its primary key.
|
|
111
|
-
*/
|
|
112
|
-
get(rawKey: Partial<T>): GetBuilder<T>;
|
|
113
|
-
/**
|
|
114
|
-
* Update an item.
|
|
115
|
-
*/
|
|
116
|
-
update(key: Partial<T>, expectedVersion?: number): UpdateBuilder<T>;
|
|
117
|
-
/**
|
|
118
|
-
* Delete an item.
|
|
119
|
-
*/
|
|
120
|
-
delete(rawKey: Partial<T>): DeleteBuilder<T>;
|
|
121
|
-
/**
|
|
122
|
-
* Query items.
|
|
123
|
-
*/
|
|
124
|
-
query(key: Partial<T>): QueryBuilder<T>;
|
|
125
|
-
/**
|
|
126
|
-
* Scan for items.
|
|
127
|
-
*/
|
|
128
|
-
scan(): ScanBuilder<T>;
|
|
55
|
+
protected getKeyValue(def: KeyDefinition<S>, rawKey: Partial<SchemaType<S>>): string;
|
|
56
|
+
buildKey(rawKey: Partial<SchemaType<S>>): Record<string, any>;
|
|
57
|
+
buildIndexes(rawItem: Partial<SchemaType<S>>): Record<string, any>;
|
|
58
|
+
create(item: SchemaType<S>): CreateBuilder<SchemaType<S>>;
|
|
59
|
+
get(rawKey: Partial<SchemaType<S>>): GetBuilder<SchemaType<S>>;
|
|
60
|
+
update(key: Partial<SchemaType<S>>, expectedVersion?: number): UpdateBuilder<SchemaType<S>>;
|
|
61
|
+
delete(rawKey: Partial<SchemaType<S>>): DeleteBuilder<SchemaType<S>>;
|
|
62
|
+
query(key: Partial<SchemaType<S>>): QueryBuilder<SchemaType<S>>;
|
|
63
|
+
scan(): ScanBuilder<SchemaType<S>>;
|
|
129
64
|
}
|
package/lib/betterddb.js
CHANGED
|
@@ -7,18 +7,15 @@ const update_builder_1 = require("./builders/update-builder");
|
|
|
7
7
|
const create_builder_1 = require("./builders/create-builder");
|
|
8
8
|
const get_builder_1 = require("./builders/get-builder");
|
|
9
9
|
const delete_builder_1 = require("./builders/delete-builder");
|
|
10
|
-
/**
|
|
11
|
-
* BetterDDB is a definition-based DynamoDB wrapper library.
|
|
12
|
-
*/
|
|
13
10
|
class BetterDDB {
|
|
14
11
|
constructor(options) {
|
|
15
12
|
var _a;
|
|
16
13
|
this.schema = options.schema;
|
|
17
14
|
this.tableName = options.tableName;
|
|
18
15
|
this.entityName = options.entityName.toUpperCase();
|
|
19
|
-
this.keys = options.keys;
|
|
20
16
|
this.client = options.client;
|
|
21
17
|
this.autoTimestamps = (_a = options.autoTimestamps) !== null && _a !== void 0 ? _a : false;
|
|
18
|
+
this.keys = options.keys;
|
|
22
19
|
}
|
|
23
20
|
getKeys() {
|
|
24
21
|
return this.keys;
|
|
@@ -35,106 +32,49 @@ class BetterDDB {
|
|
|
35
32
|
getAutoTimestamps() {
|
|
36
33
|
return this.autoTimestamps;
|
|
37
34
|
}
|
|
38
|
-
// Helper: Retrieve the key value from a KeyDefinition.
|
|
39
35
|
getKeyValue(def, rawKey) {
|
|
40
36
|
if (typeof def === 'string' || typeof def === 'number' || typeof def === 'symbol') {
|
|
41
37
|
return String(rawKey[def]);
|
|
42
38
|
}
|
|
43
|
-
|
|
44
|
-
return def.build(rawKey);
|
|
45
|
-
}
|
|
39
|
+
return def.build(rawKey);
|
|
46
40
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Build the primary key from a raw key object.
|
|
49
|
-
*/
|
|
50
41
|
buildKey(rawKey) {
|
|
51
42
|
const keyObj = {};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
typeof pkConfig.definition === 'number' ||
|
|
57
|
-
typeof pkConfig.definition === 'symbol')
|
|
58
|
-
? String(rawKey[pkConfig.definition])
|
|
59
|
-
: pkConfig.definition.build(rawKey);
|
|
60
|
-
// For sort key, if defined:
|
|
61
|
-
if (this.keys.sort) {
|
|
62
|
-
const skConfig = this.keys.sort;
|
|
63
|
-
keyObj[skConfig.name] =
|
|
64
|
-
(typeof skConfig.definition === 'string' ||
|
|
65
|
-
typeof skConfig.definition === 'number' ||
|
|
66
|
-
typeof skConfig.definition === 'symbol')
|
|
67
|
-
? String(rawKey[skConfig.definition])
|
|
68
|
-
: skConfig.definition.build(rawKey);
|
|
43
|
+
const { primary, sort } = this.keys;
|
|
44
|
+
keyObj[primary.name] = this.getKeyValue(primary.definition, rawKey);
|
|
45
|
+
if (sort) {
|
|
46
|
+
keyObj[sort.name] = this.getKeyValue(sort.definition, rawKey);
|
|
69
47
|
}
|
|
70
48
|
return keyObj;
|
|
71
49
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Build index attributes for each defined GSI.
|
|
74
|
-
*/
|
|
75
50
|
buildIndexes(rawItem) {
|
|
76
51
|
const indexAttributes = {};
|
|
77
52
|
if (this.keys.gsis) {
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
(typeof primaryConfig.definition === 'string' ||
|
|
84
|
-
typeof primaryConfig.definition === 'number' ||
|
|
85
|
-
typeof primaryConfig.definition === 'symbol')
|
|
86
|
-
? String(rawItem[primaryConfig.definition])
|
|
87
|
-
: primaryConfig.definition.build(rawItem);
|
|
88
|
-
// Compute sort index attribute if provided.
|
|
89
|
-
if (gsiConfig.sort) {
|
|
90
|
-
const sortConfig = gsiConfig.sort;
|
|
91
|
-
indexAttributes[sortConfig.name] =
|
|
92
|
-
(typeof sortConfig.definition === 'string' ||
|
|
93
|
-
typeof sortConfig.definition === 'number' ||
|
|
94
|
-
typeof sortConfig.definition === 'symbol')
|
|
95
|
-
? String(rawItem[sortConfig.definition])
|
|
96
|
-
: sortConfig.definition.build(rawItem);
|
|
53
|
+
Object.entries(this.keys.gsis).forEach(([_, gsiConfig]) => {
|
|
54
|
+
const { primary, sort } = gsiConfig;
|
|
55
|
+
indexAttributes[primary.name] = this.getKeyValue(primary.definition, rawItem);
|
|
56
|
+
if (sort) {
|
|
57
|
+
indexAttributes[sort.name] = this.getKeyValue(sort.definition, rawItem);
|
|
97
58
|
}
|
|
98
|
-
}
|
|
59
|
+
});
|
|
99
60
|
}
|
|
100
61
|
return indexAttributes;
|
|
101
62
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Create an item:
|
|
104
|
-
* - Computes primary key and index attributes,
|
|
105
|
-
* - Optionally injects timestamps,
|
|
106
|
-
* - Validates the item and writes it to DynamoDB.
|
|
107
|
-
*/
|
|
108
63
|
create(item) {
|
|
109
64
|
return new create_builder_1.CreateBuilder(this, item);
|
|
110
65
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Get an item by its primary key.
|
|
113
|
-
*/
|
|
114
66
|
get(rawKey) {
|
|
115
67
|
return new get_builder_1.GetBuilder(this, rawKey);
|
|
116
68
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Update an item.
|
|
119
|
-
*/
|
|
120
69
|
update(key, expectedVersion) {
|
|
121
70
|
return new update_builder_1.UpdateBuilder(this, key, expectedVersion);
|
|
122
71
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Delete an item.
|
|
125
|
-
*/
|
|
126
72
|
delete(rawKey) {
|
|
127
73
|
return new delete_builder_1.DeleteBuilder(this, rawKey);
|
|
128
74
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Query items.
|
|
131
|
-
*/
|
|
132
75
|
query(key) {
|
|
133
76
|
return new query_builder_1.QueryBuilder(this, key);
|
|
134
77
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Scan for items.
|
|
137
|
-
*/
|
|
138
78
|
scan() {
|
|
139
79
|
return new scan_builder_1.ScanBuilder(this);
|
|
140
80
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { DynamoDB } from 'aws-sdk';
|
|
2
2
|
import { BetterDDB } from '../betterddb';
|
|
3
|
-
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
export declare class CreateBuilder<S extends z.ZodType<any>> {
|
|
4
5
|
private parent;
|
|
5
6
|
private item;
|
|
6
7
|
private extraTransactItems;
|
|
7
|
-
constructor(parent: BetterDDB<
|
|
8
|
-
execute(): Promise<
|
|
8
|
+
constructor(parent: BetterDDB<S>, item: S);
|
|
9
|
+
execute(): Promise<S>;
|
|
9
10
|
transactWrite(ops: DynamoDB.DocumentClient.TransactWriteItemList | DynamoDB.DocumentClient.TransactWriteItem): this;
|
|
10
11
|
toTransactPut(): DynamoDB.DocumentClient.TransactWriteItem;
|
|
11
|
-
then<TResult1 =
|
|
12
|
-
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<
|
|
13
|
-
finally(onfinally?: (() => void) | null): Promise<
|
|
12
|
+
then<TResult1 = S, TResult2 = never>(onfulfilled?: ((value: S) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
13
|
+
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<S | TResult>;
|
|
14
|
+
finally(onfinally?: (() => void) | null): Promise<S>;
|
|
14
15
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { DynamoDB } from 'aws-sdk';
|
|
2
2
|
import { BetterDDB } from '../betterddb';
|
|
3
|
-
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
export declare class DeleteBuilder<S extends z.ZodType<any>> {
|
|
4
5
|
private parent;
|
|
5
6
|
private key;
|
|
6
7
|
private condition?;
|
|
7
8
|
private extraTransactItems;
|
|
8
|
-
constructor(parent: BetterDDB<
|
|
9
|
+
constructor(parent: BetterDDB<S>, key: Partial<S>);
|
|
9
10
|
/**
|
|
10
11
|
* Specify a condition expression for the delete operation.
|
|
11
12
|
*/
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { DynamoDB } from 'aws-sdk';
|
|
2
2
|
import { BetterDDB } from '../betterddb';
|
|
3
|
-
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
export declare class GetBuilder<S extends z.ZodType<any>> {
|
|
4
5
|
private parent;
|
|
5
6
|
private key;
|
|
6
7
|
private projectionExpression?;
|
|
7
8
|
private expressionAttributeNames;
|
|
8
9
|
private extraTransactItems;
|
|
9
|
-
constructor(parent: BetterDDB<
|
|
10
|
+
constructor(parent: BetterDDB<S>, key: Partial<S>);
|
|
10
11
|
/**
|
|
11
12
|
* Specify a projection by providing an array of attribute names.
|
|
12
13
|
*/
|
|
13
|
-
withProjection(attributes: (keyof
|
|
14
|
-
execute(): Promise<
|
|
14
|
+
withProjection(attributes: (keyof S)[]): this;
|
|
15
|
+
execute(): Promise<S | null>;
|
|
15
16
|
transactGet(ops: DynamoDB.DocumentClient.TransactGetItemList | DynamoDB.DocumentClient.TransactGetItem): this;
|
|
16
17
|
toTransactGet(): DynamoDB.DocumentClient.TransactGetItem;
|
|
17
|
-
then<TResult1 =
|
|
18
|
-
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<
|
|
19
|
-
finally(onfinally?: (() => void) | null): Promise<
|
|
18
|
+
then<TResult1 = S | null, TResult2 = never>(onfulfilled?: ((value: S | null) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
19
|
+
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<S | null | TResult>;
|
|
20
|
+
finally(onfinally?: (() => void) | null): Promise<S | null>;
|
|
20
21
|
}
|