{"id":16683975,"url":"https://github.com/noppoman/npdynamodb","last_synced_at":"2025-05-01T09:17:05.499Z","repository":{"id":29283362,"uuid":"32816106","full_name":"noppoMan/npdynamodb","owner":"noppoMan","description":"A Node.js Simple Query Builder and ORM for AWS DynamoDB","archived":false,"fork":false,"pushed_at":"2020-07-15T20:27:20.000Z","size":266,"stargazers_count":112,"open_issues_count":20,"forks_count":18,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-05-01T09:16:53.584Z","etag":null,"topics":["aws","dynamodb","orm","sql"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/noppoMan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-24T18:08:33.000Z","updated_at":"2024-12-10T20:56:29.000Z","dependencies_parsed_at":"2022-09-01T00:43:26.813Z","dependency_job_id":null,"html_url":"https://github.com/noppoMan/npdynamodb","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2Fnpdynamodb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2Fnpdynamodb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2Fnpdynamodb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noppoMan%2Fnpdynamodb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noppoMan","download_url":"https://codeload.github.com/noppoMan/npdynamodb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251850181,"owners_count":21653978,"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","orm","sql"],"created_at":"2024-10-12T14:27:36.678Z","updated_at":"2025-05-01T09:17:05.476Z","avatar_url":"https://github.com/noppoMan.png","language":"JavaScript","readme":"# npdynamodb [![npm version](https://badge.fury.io/js/npdynamodb.svg)](http://badge.fury.io/js/npdynamodb) [![Code Climate](https://codeclimate.com/github/noppoMan/npdynamodb/badges/gpa.svg)](https://codeclimate.com/github/noppoMan/npdynamodb) [![Build Status](https://travis-ci.org/noppoMan/npdynamodb.svg?branch=master)](https://travis-ci.org/noppoMan/npdynamodb)\nA Node.js Simple Query Builder and ORM for AWS DynamoDB.\n\n## Motivation\nWhen I visited [here ](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#query-property\n) for the first time, I closed it in a moment.\nBecause it is too long and hard to see to understand.\nSo I decided to make client to handle DynamoDB more easier and it doesn't take waste of time to read documentation for it.\n\n## Services that are used in Production\n[\u003cimg src=\"https://raw.githubusercontent.com/noppoMan/npdynamodb/gh-pages/resources/chatcast_logo.png\" width=\"250\"\u003e](https://chatca.st)\n\n\n## Supported DynamoDB Api Versions\n* 2012-08-10\n\n## Installation\n```sh\nnpm install npdynamodb\n```\n\n## Why is the Pure AWS-SDK in Node.js NOT good?\n\nParameters are like Chant of the magic.\n[http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html)\n\n## Overview\nNpdynamodb has modern interfaces to handle AWS DynamoDB.\nWe release you redundancy codes and see simple syntax.\nOf course, will not see callback hell!\n\nNpdynamodb has the following functions\n* [Simple QueryBuilder](https://github.com/noppoMan/npdynamodb/blob/master/README.md#usage-of-querybuilder)\n* [Light ORM(Active Record Model Like)](https://github.com/noppoMan/npdynamodb/blob/master/README.md#usage-of-orm)\n* [DynamoDB Migrator](https://github.com/noppoMan/npdynamodb/blob/master/README.md#migration)\n* [Command Line Interface](https://github.com/noppoMan/npdynamodb/blob/master/README.md#command-line-interfaces)\n\n### List of npdynamodb apis\n* [QueryBuilder Apis](https://github.com/noppoMan/npdynamodb/blob/master/docs/query_builder_apis.md)\n* [ORM Apis](https://github.com/noppoMan/npdynamodb/blob/master/docs/orm_apis.md)\n* [Migration Apis](https://github.com/noppoMan/npdynamodb/blob/master/docs/migration_apis.md)\n\n## Usage of QueryBuilder\n\nInitialization\n```js\nvar npdynamodb = require('npdynamodb');\nvar AWS = require('aws-sdk');\n\nvar dynamodb = new AWS.DynamoDB({\n  apiVersion: '2012-08-10'\n});\n\nvar npd = npdynamodb.createClient(dynamodb);\n\n// Or can take options\nvar npd = npdynamodb.createClient(dynamodb, {\n  timeout: 3000,\n  initialize: function(){\n    // Some Initialization here.\n  }\n});\n```\n\n##### Get by hash key (getItem operation)\n```js\nnpd().table('users')\n.where(\"id\", 1)\n.first()\n.then(function(data){\n\n  console.log(data)\n  // =\u003e {Item: {id: 1, name: 'Tonny'}, Count: 1, ScannedCount: 1}\n\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### Get rows with where (query operation)\n```js\nnpd().table('users')\n.where('name', 'tonny') //hash key\n.then(function(data){\n\n  console.log(data)\n  // =\u003e {Items: [{id: 1, name: 'Tonny'}], Count: 1, ScannedCount: 1}\n\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### Get multiple rows with where, filter and descending order\n```js\nnpd().table('chats')\n.where('room_id', 'room1') // hash key\n.where('timestamp', '\u003e', 1429454911) // range key\n.filter('user_name', 'tonny') // non index key\n.desc()\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### whereIn\nwhereIn call batchGetItem instead of query operation.\n\n###### Single Key Usage\n```js\nnpd().table('chats')\n.whereIn('room_id', ['room1', 'room2', 'room3'])\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n###### Multiple Kyes Usage\n```js\nnpd().table('chats')\n.whereIn(['room_id', 'timestamp'], [['room1', 1429454911], ['room2', 1429454912], ['room3', 1429454913]])\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### Limit and offset\n```js\nnpd().table('chats')\n.where('room_id', 'room1')\n.limit(10)\n.offset(ExclusiveStartKey)\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### Count\n```js\nnpd().table('chats')\n.where('room_id', 'room1')\n.count()\n.then(function(data){\n  console.log(data.Count);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### Extra options\nYou can set extra options in callback of `feature` method. All options are transformed from property to method, But its name (camelized) and arguments are same as pure AWS-SDK for node.js.\n\n```js\nnpd().table('users')\n.where('name', 'tonny')\n.feature(function(f){ // f is raw feature object.\n  f.consistentRead(true);\n  f.returnConsumedCapacity('TOTAL');\n})\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### create (Make Overwrite all of values, if key[s] have already existed.)\n```js\nnpd().table('users')\n.create({ // Also can save collection.\n  id: 2,\n  name: 'rhodes',\n  company: {\n    name: 'Stark Industry',\n    tel: '123456789',\n    zip: '123456789',\n    address: 'foo-bar-123'\n  }\n})\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n##### Update\n```js\nnpd().table('users')\n.set(\"company\", \"PUT\", {\n  name: 'moved company',\n  tel: '123-456-789',\n  zip: '123-456-789',\n  address: 'foo-bar-456'\n})\n.set(\"suite_color\", \"ADD\", 1)\n.update()\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n```\n\n\n##### Update with expressions\n```js\nnpd().table('users')\n.feature(function(f){\n  f.updateExpression('SET #gt = if_not_exists(#gt, :one)');\n\n  f.expressionAttributeNames({\n    '#gt': 'gender_type'\n  });\n\n  f.expressionAttributeValues({\n    ':one': 1\n  });\n\n  f.returnValues('UPDATED_NEW');\n})\n.update()\n.then(function(data){\n  console.log(data);\n})\n.catch(function(err){\n  console.err(err);\n});\n\n```\n\n## Usage of ORM\n\nInitialization\n```js\nvar npdynamodb = require('npdynamodb');\nvar AWS = require('aws-sdk');\n\nvar npd = npdynamodb.createClient(new AWS.DynamoDB({\n  apiVersion: '2012-08-10'\n}));\n\nvar Chat = npdynamodb.define('chats', {\n  npdynamodb: npd,\n\n  hashKey: 'id',\n\n  rangeKey: 'timestamp'\n});\n```\n\n##### Fast get with hash_key\n```js\nChat.find(1).then(function(chat){  // where('id', '=', 1)\n  // Get value of id key\n  console.log(chat.get('id'));\n\n  // Get attribute keys\n  console.log(chat.keys());\n\n  // Get attribute values\n  console.log(chat.values());\n\n  // Pick specified key and value pairs\n  console.log(chat.pick('chat_id', 'timestamp'));\n\n  // Transform as json string.\n  console.log(chat.toJson());\n});\n```\n\n##### fetch with multiple conditions\n```js\nChat.where('id', 1)\n// complex conditions\n.query(function(qb){\n  qb.whereBeteen('timestamp', 1429212102, 1429212202);\n})\n.fetch()\n.then(function(data){\n\n  // Check query result is empty?\n  console.log(data.isEmpty());\n  // =\u003e false\n\n  // Get First Item\n  console.log(data.first().get('id'));\n  // =\u003e 1\n\n  // Get Last Item\n  console.log(data.last().get('id'));\n  // =\u003e 1\n\n  // Seequence(Also supported map, find, etc....)\n  data.each(function(item){\n    console.log(item.get('id'));\n  });\n\n  // Pluck specific column values.\n  console.log(data.pluck('id'));\n\n  // Get as object.\n  console.log(data.toArray());\n  // =\u003e [{id: 1, name: 'tonny', company: {....}}]\n\n});\n```\n\n##### Save\n```js\n// As Static\nChat.save({\n  room_id: 'room1',\n  ....\n})\n.then(function(chat){\n  console.log(chat.get('room_id'));\n});\n\n// As Instance\nvar chat = new Chat({\n  room_id: 'room1',\n  user_id: 1\n});\nchat.set('message', 'This is a message.');\n\nchat.save()\n.then(function(chat){\n  console.log(chat.get('room_id'));\n});\n```\n\n##### Destroy\n```js\nchat.destroy()\n.then(function(data){\n  console.log(data);\n});\n```\n\n##### Custom Methods and Properties\n```js\nvar Chat = npdynamodb.define('chats', {\n  npdynamodb: npd,\n\n  hashKey: 'id',\n\n  rangeKey: 'timestamp',\n\n  customProtoTypeConstant: 1,\n\n  customProtoTypeMethod: function(){\n    return this.get('id') === 1;\n  }\n\n},\n\n{\n  customStaticConstant: 1,\n\n  customStaticMethod: function(){\n    return this.where('room_id', 'room1')\n      .query(function(qb){\n        qb.filter('timestamp', '\u003e', 1429212102);\n      })\n      .fetch();\n  }\n});\n\n// prototype\nChat.find(1).then(function(chat){\n  console.log(chat.customProtoTypeConstant);\n  console.log(chat.customeProtoTypeMethod());\n});\n\n\n// static\nconsole.log(Chat.customStaticConstant);\n\nChat.customStaticMethod().then(function(data){\n  console.log(data);\n});\n```\n\n## Migration\nWe support schema migration for Dynamodb.\n\n##### First, initialize your project to run migration.\n```sh\nnpm install -g npdynamodb\n# cd /path/to/your/project\nnpd init\n# created npdfile.js\n```\n\n##### npdfile.js\n```js\n'use strict';\n\nvar AWS = require('aws-sdk');\n\nvar dynamodb = new AWS.DynamoDB({\n  apiVersion: '2012-08-10',\n  accessKeyId: \"AWS_KEY\",\n  secretAccessKey: \"AWS_SECRET\",\n  region: \"ap-northeast-1\"\n});\n\nmodule.exports = {\n\n  // Specify migration file path. Default is `./migrations`\n  // migration: {\n  //  migrationFilePath: './npdynamodb_migrations'\n  // },\n\n  development: {\n    dynamoClient: dynamodb,\n    migrations: {\n      ProvisionedThroughput: [10, 10],\n      tableName: 'npd_migrations'\n    }\n  },\n\n  staging: {\n    dynamoClient: dynamodb,\n    migrations: {\n      ProvisionedThroughput: [10, 10],\n      tableName: 'npd_migrations'\n    }\n  },\n\n  production: {\n    dynamoClient: dynamodb,\n    migrations: {\n      ProvisionedThroughput: [10, 10],\n      tableName: 'npd_migrations'\n    }\n  }\n};\n```\n\n##### Generate migration file.\n```sh\nnpd migrate:generate create_users\n# =\u003e /migrations/20150406083039_create_users.js\n```\n\n##### Edit migration file\n```js\nexports.up = function(migrator){\n  return migrator().createTable('chats', function(t){\n    t.string('room_id').hashKey();\n    t.number('timestamp').rangeKey();\n    t.provisionedThroughput(100, 100); // read, write\n\n    t.globalSecondaryIndex('indexName1', function(t){\n      t.string('user_id').hashKey();\n      t.provisionedThroughput(100, 100); // read, write\n      t.projectionTypeAll(); //default is NONE\n    });\n\n    t.localSecondaryIndex('indexName2', function(t){\n      t.string('room_id').hashKey();\n      t.number('user_id').rangeKey();\n      t.projectionTypeAll(); //default is NONE\n    });\n  });\n};\n\nexports.down = function(migrator){\n  return migrator().deleteTable('chats');\n};\n```\n\n##### UpdateTable Usage\n```js\nexports.up = function(migrator, config){\n  return migrator().updateTable('test_table1', function(t){\n    t.globalSecondaryIndexUpdates(function(t){\n\n      t.create('indexName3', function(t){\n        t.string('hash_key2').hashKey();\n        t.provisionedThroughput(100, 100);\n        t.projectionTypeAll();\n      });\n\n      t.delete('indexName2');\n\n      t.update('indexName1', function(t){\n        t.provisionedThroughput(150, 100);\n      });\n\n      t.provisionedThroughput(200, 200);\n\n    });\n  }).then(function(){\n    // wait until tables state will be ACTIVE.\n    return migrator().waitUntilTableActivate('test_table1');\n  });\n};\n```\n\n##### Run latest migration.\n```sh\nnpd migrate:run\n```\n\n##### Rollback latest migration.\n```sh\nnpd migrate:rollback\n```\n\n## Command Line Interfaces\n#### required global install and type `npd`\n### Commands\n* `init`: Create a fresh npdfile.js.\n* `migrate:generate \u003cname\u003e` Create a named migration file.\n* `migrate:run` Run all migrations that have not yet been run.\n* `migrate:rollback` Rollback the last set of migrations performed.\n* `listTables` List existing tables.\n* `dump \u003ctable\u003e`: Dump amount of records in specified table to stdout.\n* `desc \u003ctable\u003e`: Show result of the describe operation\n* `get \u003ctable\u003e \u003chashKey\u003e [rangeKey]`: Show results of the query operation by given conditions.\n* `dropTable \u003ctable\u003e`: Drop the specified table.\n\n### Global Options\n* `-h`\n* `-V`\n* `--env`\n\n## How to test?\n```sh\nnpm test\n```\n\n## QueryBuilder Callbacks\nYou can be hooked several events and their can be taken promise.\n\nMechanism of Callbacks and Events\n```\noperation called.\n      ↓\ncallbacks: beforeQuery\n      ↓\nevents: beforeQuery\n      ↓\nSending Request to Dynamodb\n      ↓\nGetting Response from Dynamodb\n      ↓\ncallbacks: afterQuery\n      ↓\nevents: afterQuery\n```\n\n```js\n// Register callbacks globally\nvar npd = npdynamodb.createClient(dynamodb, {\n  initialize: function(){\n    this.callbacks('beforeQuery', function(){\n      if(this._fature.params['hash_key'] !== 1) {\n        return Promise.reject(new Error('invalid value'));\n      }\n    });\n\n    this.callbacks('afterQuery', function(result){\n      return npd().table('related').create({\n        foo_id: result.Items[0]['hash_key'],\n        bar: 'string value'\n      });\n    });\n  }\n});\n\n// Register callbacks at only this time.\nnpd().table('foo').callbacks('beforeQuery', Func).create({\n  hoo: 'hoo',\n  bar: 'bar'\n});\n```\n\n## Plugin and Extending\nNpdynamodb can be extended via plugins.\n\n```js\nnpdynamodb.plugin(function(Klass){\n\n  // Extend QueryBuilder\n  Klass.QueryBuilder.extend({\n    protoFn: function(){\n      console.log('foo');\n    }\n  },\n  {\n    staticFn: function(){\n      console.log('bar');\n    }\n  });\n\n  // Extend Orm Collection\n  Klass.Collection.extend({\n    protoFn: function(){\n      console.log('foo');\n    }\n  },\n  {\n    staticFn: function(){\n      console.log('bar');\n    }\n  });\n\n  // Extend Orm Model\n  Klass.Model.extend({\n    protoFn: function(){\n      console.log('foo');\n    }\n  },\n  {\n    staticFn: function(){\n      console.log('bar');\n    }\n  });\n\n});\n```\n\n### Available Plugins\n* [npdynamodb-typecast](https://github.com/noppoMan/npdynamodb-typecast) For casting hash and range key to actual attribution type\n\n## Browser Support\nNpdynamodb can be built using browserify or webpack, and pre-built or pre-built with uglified version can be found in the build directory.\n\n### For Browserify or Webpack\n```js\nvar AWS = require('aws-sdk');\nvar npdynamodb = require('npdynamodb/build/npdynamodb');\n\nvar dynamodb = new AWS.DynamoDB({\n  apiVersion: '2012-08-10',\n  accessKeyId: \"here is key\",\n  secretAccessKey: \"here is secret key\",\n  region: \"ap-northeast-1\",\n  sslEnabled: true,\n});\n\nvar npd = npdynamodb.createClient(dynamodb);\nnpd().table('table_name').where('id', 1).then(function(data){\n  console.log(data);\n});\n```\n\n### For HTML\n```html\n\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/aws-sdk/2.1.39/aws-sdk.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.0/lodash.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.33/bluebird.min.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"../build/npdynamodb.min.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  var dynamodb = new AWS.DynamoDB({\n    apiVersion: '2012-08-10',\n    accessKeyId: \"here is key\",\n    secretAccessKey: \"here is secret key\",\n    region: \"ap-northeast-1\",\n    sslEnabled: true,\n  });\n\n  var npd = npdynamodb.createClient(dynamodb);\n  npd().table('table_name').where('id', 1).then(function(data){\n    console.log(data);\n  });\n\u003c/script\u003e\n```\n\n## Upgrading and Release Note\n#### Upgrading 0.1x -\u003e 0.2x\n\n##### QueryBuilder\nThere should be a minor change for QueryBuilder. 0.2x  QueryBuilder can take options as second argument of createClient.\n* 0.2.0: `timeout` option supported.\n* 0.2.6: `initialize` option and [callbacks](#querybuilder-callbacks) supported.\n* 0.2.7: [whereIn](wherein) method supported.\n\n##### ORM\nThere should be a major change for ORM. 0.2x ORM constructor need to pass the npdynamodb instance instead of pure dynamodb instance.\n* 0.2.7: Supported to parse `whereIn` results.\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2015 Yuki Takei(Noppoman) yuki@miketokyo.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and marthis permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoppoman%2Fnpdynamodb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoppoman%2Fnpdynamodb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoppoman%2Fnpdynamodb/lists"}