ts-dynamodb-attributes-transformer: A code transformer of TypeScript object to DynamoDB attributes
Tags: typescript, dynamodb
Published: 2022-10-17 06:14:14

I published "ts-dynamodb-attributes-transformer" that transforms the TypsScript object to Amazon DynamoDB attributes (precisely, this attributes type is `Record<string, AttributeValue>` that is defined at [aws-sdk-js-v3](https://github.com/aws/aws-sdk-js-v3)):

https://github.com/moznion/ts-dynamodb-attributes-transformer

And this is also available on npm:

https://www.npmjs.com/package/@moznion/ts-dynamodb-attributes-transformer

ts-dynamodb-attributes-transformer is a code transformer plugin that uses [TypeScript Compiler API](https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API).

Let's see how this transformer works.
When it gives the following TypeScript code to this transformer,

```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<string, string>;
}

const record: Record<keyof User, AttributeValue> = dynamodbRecord<User>({
  id: 12345,
  name: 'John Doe',
  tags: new Map<string, string>([
    ['foo', 'bar'],
    ['buz', 'qux'],
  ]),
});
```

This transformer detects `dynamodbRecord<T>(obj: T)` (in this case that is `dynamodbRecord<User>(...)`) function invocation and analyzes the fields definition of the type parameter `T` (in this case `User`), and it generates the JavaScript code like the following according to the T-typed object given as a parameter.

```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 behavior is like it replaces `dynamodbRecord<T>(obj: T)` invocation with a generator function for DynamoDB attributes entirely.

As I noted above, this is a TypeScript transformer plugin so it needs to hook this plugin on TypeScript compilation phase. I suppose one of the easiest ways is using [ttypescript](https://github.com/cevek/ttypescript) which is a custom TypeScript compiler that can hook the transformer plugin on a compilation. Please refer also to the ["How to use this transformer"](https://github.com/moznion/ts-dynamodb-attributes-transformer#how-to-use-this-transformer) section in README.

Naturally, this plugin can transform a target object to the DynamoDB attributes according to the type definition of that object. And this uses TypeScript Compiler API to transform the code thus it can determine the code to generate on compilation statically so it doesn't have to do reflection or something on the fly to construct the DynamoDB attributes object. This contributes to good performance.

The benchmark result between this project and [kayomarz/dynamodb-data-types](https://github.com/kayomarz/dynamodb-data-types) is the following:

```
node version: v16.17.0
dynamodb-data-types marshalling x 3,475,450 ops/sec ±0.45% (96 runs sampled)
ts-dynamodb-attributes-transformer marshalling x 13,405,409 ops/sec ±0.43% (91 runs sampled)
Fastest is ts-dynamodb-attributes-transformer marshalling
```

This shows this transformer is about 3.85 times faster than the dynamodb-data-types.


At this moment, this plugin satisfies my use case but I would appreciate your suggestions if you have some enhancement ideas or find any issues!