Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/moznion/ts-dynamodb-attributes-transformer
Code transformer plugin powered by TypeScript Compiler API that transforms TypeScript object from/to Amazon DynamoDB attributes
https://github.com/moznion/ts-dynamodb-attributes-transformer
compiler-api dynamodb typescript
Last synced: 2 months ago
JSON representation
Code transformer plugin powered by TypeScript Compiler API that transforms TypeScript object from/to Amazon DynamoDB attributes
- Host: GitHub
- URL: https://github.com/moznion/ts-dynamodb-attributes-transformer
- Owner: moznion
- License: mit
- Created: 2022-10-16T02:25:10.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-10-23T21:49:25.000Z (3 months ago)
- Last Synced: 2024-10-24T10:27:06.017Z (3 months ago)
- Topics: compiler-api, dynamodb, typescript
- Language: TypeScript
- Homepage:
- Size: 303 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ts-dynamodb-attributes-transformer [![.github/workflows/check.yml](https://github.com/moznion/ts-dynamodb-attributes-transformer/actions/workflows/check.yml/badge.svg)](https://github.com/moznion/ts-dynamodb-attributes-transformer/actions/workflows/check.yml) [![npm version](https://badge.fury.io/js/@moznion%2Fts-dynamodb-attributes-transformer.svg)](https://badge.fury.io/js/@moznion%2Fts-dynamodb-attributes-transformer)
Code transformer plugin powered by [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) that transforms TypeScript object from/to Amazon DynamoDB attributes.
## Description
This plugin replaces the TypeScript function invocation with the generated object code. In short, this plugin generates the code for every property of type `T`.
### `dynamodbRecord(obj: T, shouldLenientTypeCheck?: boolean): Record`
This plugin replaces `dynamodbRecord(obj: T)` invocation with `Record` value that is defined in aws-sdk-js-v3 according to the type `T` and the contents of the object.
This plugin powers the users can do drop-in replacements for the existing `Record` value and/or the generator with `dynamodbRecord(obj: T)` function.
### `fromDynamodbRecord(attrs: Record, shouldLenientTypeCheck?: boolean): T`
This replaces `fromDynamodbRecord(attrs: Record)` invocation with the object which has type `T`. This method is responsible to translate the DynamoDB attributes to the actual TypeScript object, i.e. unmarshalling.
## Motivations
- To do automatic generation of the [DynamoDB attribute data type](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html) code that is recognizable by [aws-sdk-js-v3](https://github.com/aws/aws-sdk-js-v3), with type safety.
- Manual making the translation layer between the object and DynamoDB's Record is no longer needed!
- Performance. This uses TypeScript Compiler API, so it generates/determine the DynamoDB attribute code at the compiling timing. This means the logic doesn't have to do a reflection on the fly so this contributes to a good performance.### Benchmark
The benchmark result between this project and [kayomarz/dynamodb-data-types](https://github.com/kayomarz/dynamodb-data-types) is the following:
marshalling:
```
node version: v16.17.0
dynamodb-data-types marshalling x 3,845,247 ops/sec ±0.63% (90 runs sampled)
ts-dynamodb-attributes-transformer marshalling x 13,614,974 ops/sec ±0.24% (100 runs sampled)
Fastest is ts-dynamodb-attributes-transformer marshalling
```unmarshalling:
```
node version: v16.17.0
dynamodb-data-types unmarshalling x 1,800,718 ops/sec ±0.30% (96 runs sampled)
ts-dynamodb-attributes-transformer unmarshalling x 3,493,272 ops/sec ±0.50% (98 runs sampled)
Fastest is ts-dynamodb-attributes-transformer unmarshalling
```Please see also [benchmark](./examples/benchmark) project.
## Synopsis
### Marshalling into DynamoDB record from the Typescript Object
```ts
import { AttributeValue } from '@aws-sdk/client-dynamodb';
import { dynamodbRecord } from '@moznion/ts-dynamodb-attributes-transformer';interface User {
readonly id: number;
readonly name: string;
readonly tags: Map;
}const record: Record = dynamodbRecord({
id: 12345,
name: 'John Doe',
tags: new Map([
['foo', 'bar'],
['buz', 'qux'],
]),
});/*
* Then you can use this record value on the aws-sdk-js-v3's DynamoDB client; for example,
*
* const dyn = new DynamoDBClient(...);
* await dyn.send(new PutItemCommand({
* TableName: "...",
* Item: record, // <= HERE!
* }));
*/
```Then this plugin transforms the above TypeScript code like the following JavaScript code:
```js
const record = (function (arg) {
return {
id: {
N: arg.id.toString()
},
name: {
S: arg.name
},
tags: {
M: (function () {
var m;
m = {}
for (const kv of arg.tags) {
m[kv[0]] = { S: kv[1] }
}
return m;
})()
}
};
})({
id: 12345,
name: 'John Doe',
tags: new Map([
['foo', 'bar'],
['buz', 'qux'],
]),
});
/*
* This record is equal to the following object:
*
* {
* id: { N: "12345" },
* name: { S: "John Doe" },
* tags: {
* M: {
* foo: { S: "bar" },
* buz: { S: "qux" }
* }
* }
* }
*/
```### Unmarshalling into TypeScript object from DynamoDB record
```ts
import { fromDynamodbRecord } from '@moznion/ts-dynamodb-attributes-transformer';interface User {
readonly id?: number;
readonly name?: string;
readonly tags: Map;
}const user: User = fromDynamodbRecord({
id: {
N: '12345',
},
name: {
S: 'John Doe',
},
tags: {
M: {
foo: {
S: 'bar',
},
buz: {
S: 'qux',
},
},
},
});
```Then this plugin transforms the above TypeScript code like the following JavaScript code:
```js
const user = (function (arg) {
return {
id: (function () {
const numStr = arg.id.N;
return numStr === undefined ? undefined : Number(numStr);
})(),
name: arg.name.S,
tags: (function () {
var m, r;
m = new Map();
r = arg['tags']?.M;
for (const k in r) {
m.set(k, r[k]?.S);
}
return m;
})(),
};
})({
id: {
N: '12345',
},
name: {
S: 'John Doe',
},
tags: {
M: {
foo: {
S: 'bar',
},
buz: {
S: 'qux',
},
},
},
});/*
* This object is equal to the following:
*
* {
* id: 12345,
* name: "John Doe",
* tags: {
* foo: { S: "bar" },
* buz: { S: "qux" },
* }
* }
*/
```## How to use this transformer
This plugin exports the functions that have the signature `function dynamodbRecord(item: T, shouldLenientTypeCheck?: boolean): Record` and `function fromDynamodbRecord(attrs: Record, shouldLenientTypeCheck?: boolean): T`.
These functions are the markers to indicate to the transformer to replace the function invocation with the generated code. Therefore, there are some restrictions:
- Type parameter `T` is mandatory parameter (i.e. this mustn't be omitted). A transformer analyzes the type of the given `T` to collect the property information.
- Type `T` must be class or interface. If it needs to do unmarshalling, this type `T` must be a derived type of the **interface**.### Examples
#### ttypescript
[ttypescript](https://github.com/cevek/ttypescript) is a custom TypeScript compiler that triggers the specified transformers in the tsconfig.json.
Please refer to the [examples/ttypescript](./examples/ttypescript) project directory and [ttypescript official README](https://github.com/cevek/ttypescript) for more details.
Anyway, the important thing is specifying `compilerOptions.plugins` in tsconfig.json like the following:
```json
{
"compilerOptions": {
// ...
"plugins": [
{ "transform": "@moznion/ts-dynamodb-attributes-transformer/transformer" }
]
},
// ...
}
```#### ts-jest
If you use [ts-jest](https://github.com/kulshekhar/ts-jest) with this transformer, one of the easiest ways is using that with ttypescript toghether.
It needs ttypescript configuration and additionally the jest configuration in `jest.config.js` like the below:
```js
module.exports = {
// ...
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
compiler: 'ttypescript',
},
],
},
// ...
};```
## TypeScript types to DynamoDB types
Please see also [Supported data types and naming rules in Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html) for more details about the DynamoDB types.
### Scalar Types
| TypeScript | DynamoDB |
|----------------|------------|
| number, BigInt | N |
| string | S |
| Uint8Array | B |
| boolean | BOOL |
| unknown | NULL |NOTE: if the TypeScript property has `unknown` type and the value is `null` then DynamoDB attribute becomes `{ NULL: true }`. Else, that attribute value is `{ NULL: false }`.
### Document Types
| TypeScript | DynamoDB |
|----------------------------------------------------------------|----------|
| `Set` | SS |
| `Set`, `Set` | NS |
| `Set` | BS |
| `List<$SCALAR_TYPE>` | L |
| `Map`, `{ [key: string]: $SCALAR_TYPE }` | M |## Options
### Lenient type checking (default: `false`)
By default, if this plugin encounters unsupported types, it raises the error and halts the transformation.
But if `true` value is given through the second argument of the function, it proceeds the transformation with ignoring the unsupported typed property even if it gets the unsupported types.
## Note
This transformer plugin referred to the various things from [kimamula/ts-transformer-keys](https://github.com/kimamula/ts-transformer-keys)
## Authors
moznion ()