{"id":13528379,"url":"https://github.com/balmbees/dynamo-types","last_synced_at":"2025-04-08T10:33:08.604Z","repository":{"id":44839373,"uuid":"97037164","full_name":"balmbees/dynamo-types","owner":"balmbees","description":"Typescript AWS DynamoDB ORM","archived":false,"fork":false,"pushed_at":"2025-02-19T02:19:32.000Z","size":972,"stargazers_count":232,"open_issues_count":28,"forks_count":18,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-01T09:26:49.205Z","etag":null,"topics":["aws-dynamodb","dynamodb","javascript","orm","serverless","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/balmbees.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-07-12T17:51:44.000Z","updated_at":"2025-03-24T04:03:46.000Z","dependencies_parsed_at":"2024-01-13T15:37:38.168Z","dependency_job_id":"0da77fd1-f05f-408f-84c8-3331d0bc42d6","html_url":"https://github.com/balmbees/dynamo-types","commit_stats":{"total_commits":235,"total_committers":12,"mean_commits":"19.583333333333332","dds":0.7148936170212765,"last_synced_commit":"a07bf6fda141fe31f66c1665e360aa39050a04eb"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/balmbees%2Fdynamo-types","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/balmbees%2Fdynamo-types/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/balmbees%2Fdynamo-types/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/balmbees%2Fdynamo-types/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/balmbees","download_url":"https://codeload.github.com/balmbees/dynamo-types/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247824027,"owners_count":21002191,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-dynamodb","dynamodb","javascript","orm","serverless","typescript"],"created_at":"2024-08-01T06:02:29.060Z","updated_at":"2025-04-08T10:33:08.580Z","avatar_url":"https://github.com/balmbees.png","language":"TypeScript","readme":"[![Build Status](https://github.com/balmbees/dynamo-types/workflows/workflow/badge.svg)](https://github.com/balmbees/dynamo-types/actions)\n[![npm version](https://badge.fury.io/js/dynamo-types.svg)](https://badge.fury.io/js/dynamo-types)\n[![Semantic Release enabled](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/)\n\n\n# DynamoTypes\n\nTypescript ORM of DynamoDB, written from scratch to fully support DynamoDB. Powering [Vingle](https://www.vingle.net)\n\n## Features\n1. Serialize / Deserialize DynamoDB record -\u003e TS class object based on annotations.\n2. Table Configurations\n   - CreateTable\n     - Create secondary indexes (Both local / global)\n     - Configure TTL\n   - DropTable\n3. PrimaryKey\n   - FullPrimaryKey (Hash, Range)\n   - HashPrimaryKey (Hash)\n4. Indexes\n   - Local, both hash and range key\n   - Global, both hash and range key\n5. Attribute\n   - Type Support (Number / String / Boolean / Array / Object / Buffer)\n   - TimeToLive\n6. DAX Support\n   - You can specify this by setting the connection of table. \n7. Optimized aws-sdk usage\n   - aws-sdk has a serious problem of not reusing HTTP connection towards DynamoDB by default. check [this issue](https://github.com/aws/aws-sdk-js/issues/900)\n   - this could cause unbearable latency sometimes with showing \u003e 100ms. it's more of an issue of NodeJS HTTP module but nevertheless, it has been optimized here by keep-alive [Code](https://github.com/balmbees/dynamo-types/blob/master/src/connections/dynamodb_connection.ts#L37)\n8. AWS X-Ray support\n   - XRay is serverless distributed tracing service. In order to log DynamoDB transaction into it, you also need to some sort of risk monkey-patching. Here you can turn it on by setting [`process.env.ENABLE_XRAY = \"true\"`](https://github.com/balmbees/dynamo-types/blob/e0391c1c171638d06f9262446d8cbcb14a573cc8/src/config.ts#L9)\n9. Testing\n   - You can change the endpoint of DynamoDB by setting the environment variable or setting new connection, So you can install [local-dynamo](https://www.npmjs.com/package/local-dynamo) locally at setup endpoint to local. refer package.json for the detailed how-to\n\nAlso, dynamo-types let you overcome several limits that DynamoDB or the aws-sdk has.\n\n1. BatchWrite (batchDelete / batchPut) has a limit of a maximum of 25 items per request.\n   - dynamo-types automatically splits given items into chunks of 25 and sends requests in parallel\n2. BatchGet has a limit of a maximum of 100 items per requests\n   - dynamo-types automatically splits given keys to chunks of 100 and sends requests in parallel\n3. BatchGet doesn't keep the order of items as it is in input keys,\n   - dynamo-types sort return items based on input keys\n4. BatchGet doesn't handle \"missing items\".\n   - dynamo-types has \"BatchGet\" / \"BatchGetFull\" \n     - BatchGet  \n        order items follow to keys, missing items are just missing. return type Promise\u003cArray\u003cItem\u003e\u003e  \n        so keys.legnth !== items.keys in this case  \n     - BatchGetFull   \n        order items follow to keys, fill missing items with \"null\". return type Promise\u003cArray\u003cItem | null\u003e\u003e  \n        so keys.length === items.keys always true  \n\nAnd most importantly, all of those queries regardless of whether it's from index or primary key, strongly typed. I mean what's the point of using typescript if not anyway?\n\n## Usage\n```typescript\n  @Decorator.Table({ name: \"prod-Card\" })\n  class Card extends Table {\n    @Decorator.Attribute()\n    public id: number;\n\n    @Decorator.Attribute()\n    public title: string;\n\n    @Decorator.Attribute({ timeToLive: true })\n    public expiresAt: number;\n\n    @Decorator.FullPrimaryKey('id', 'title')\n    static readonly primaryKey: Query.FullPrimaryKey\u003cCard, number, string\u003e;\n\n    @Decorator.Writer()\n    static readonly writer: Query.Writer\u003cCard\u003e;\n  }\n\n  // Create Table At DynamoDB\n  await Card.createTable();\n\n  // Drop Table At DynamoDB\n  await Card.dropTable();\n\n\n  // Creating Record\n  const card = new Card();\n  card.id = 100;\n  card.title = \"Title\";\n  //\n  await Card.writer.put(card);\n  // OR just\n  await card.save();\n\n  // Batch Put\n  await Card.writer.batchPut([\n    new Card(),\n    new Card()\n  ]);\n\n  // Get Record\n  await Card.primaryKey.get(100, \"Title\");\n\n  // BatchGet\n  // This array is strongly typed such as Array\u003c[number, string]\u003e so don't worry.\n  await Card.primaryKey.batchGet([\n    [100, \"Title\"],\n    [200, \"Title2\"]\n  ])\n\n  // Query\n  // Range key opreations are stringly typed. ([\"\u003e=\", T] | [\"=\", T] ...)\n  await Card.primaryKey.query({\n    hash: 100,\n    range: [\"\u003e=\", \"Title\"]\n  })\n\n  // Delete record\n  await card.delete()\n\n  // Delete record only when it meets condition.\n  // with this, you can avoid race condition such as somebody updating the record while you're trying to delete it\n  await card.delete({\n    condition: { title: Equal(\"Title\") }\n  });\n  // when mismatch occurs, it raises \"ConditionalCheckFailedException\" error.\n\n  // Likewise, update record only when it meets condition\n  card.title = \"New Title\"\n  await card.save({ condition: { title: \"Title\" } });\n  // when mismatch occurs, it raises \"ConditionalCheckFailedException\" error.\n```\n\n\n```typescript\nimport {\n  Config,\n  Decorator,\n  Query,\n  Table,\n} from \"dynamo-types\";\n\n@Decorator.Table({ name: `table_name` })\nexport class CardStat extends Table {\n  @Decorator.HashPrimaryKey(\"card_id\")\n  public static readonly primaryKey: Query.HashPrimaryKey\u003cCardStat, number\u003e;\n\n  @Decorator.Writer()\n  public static readonly writer: Query.Writer\u003cCardStat\u003e;\n\n  @Decorator.Attribute({ name: \"card_id\" })\n  public cardId: number;\n\n  @Decorator.Attribute({ name: \"impressions_count\" })\n  public impressionsCount: number = 0;\n\n  @Decorator.Attribute({ name: \"shares\" })\n  public shares: number = 0;\n}\n```\n\n### TS Compiler Setting\nDynamoTypes utilize [reflect-metadata](https://github.com/rbuckton/reflect-metadata) to read metadata (usually type of variables) from Typescript code. to do so, you must enable those options.\n\n```json\n{\n    \"compilerOptions\": {\n        // other options..\n        //\n        \"experimentalDecorators\": true, // required\n        \"emitDecoratorMetadata\": true // required\n    }\n}\n```\n\n### Connection\nDynamoDB supports 2 different kinds of connections. Plain connections to DynamoDB through HTTP, or through DAX.\ndynamo-types supports this by letting you create a separate connection for each table.\n\n```typescript\n@Decorator.Table({ name: \"prod-Card1\", connection: new DAXConnection({ endpoints: [\"dax-domain:8892\"] }) })\nclass Card extends Table {\n  @AttributeDecorator()\n  public id: number;\n\n  @AttributeDecorator()\n  public title: string;\n\n  @AttributeDecorator({ name: \"complicated_field\"})\n  public complicatedField: string;\n\n  @FullPrimaryKeyDecorator('id', 'title')\n  static readonly primaryKey: Query.FullPrimaryKey\u003cCard, number, string\u003e;\n\n  @WriterDecorator()\n  static readonly writer: Query.Writer\u003cCard\u003e;\n}\n```\n\nThen any query that is sent to the Card table will be sent through DAXConnection.\n\nIf you don't specify any connection, it automatically uses [default connection](https://github.com/balmbees/dynamo-types/blob/e0391c1c171638d06f9262446d8cbcb14a573cc8/src/config.ts#L5), which is [DynamoDBConnection](https://github.com/balmbees/dynamo-types/blob/e0391c1c171638d06f9262446d8cbcb14a573cc8/src/connections/dynamodb_connection.ts).\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbalmbees%2Fdynamo-types","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbalmbees%2Fdynamo-types","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbalmbees%2Fdynamo-types/lists"}