47

I want to use the aws-sdk in JavaScript using promises.

Instead of the default callback style:

dynamodb.getItem(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});

I instead want to use a promise style:

dynamoDb.putItemAsync(params).then(function(data) {
  console.log(data);           // successful response
}).catch(function(error) {
  console.log(err, err.stack); // an error occurred
});
Jason
  • 9,408
  • 5
  • 36
  • 36
Martin Kretz
  • 1,523
  • 2
  • 13
  • 20

8 Answers8

32

The 2.3.0 release of the AWS JavaScript SDK added support for promises: http://aws.amazon.com/releasenotes/8589740860839559

Majix
  • 1,702
  • 1
  • 16
  • 8
  • 4
    Which unfortunately isn't supported under Lambda. :-( – Jason Apr 15 '16 at 10:29
  • 2
    So, I've been trying to get this to work in Lambda and just found your comment. Why doesn't this work in Lambda? Where did you find that information? Thanks! – Lereveme May 31 '16 at 03:13
  • 3
    It should work under lambda if you use node 4.3.2 and you can even change the Promise library. If you're using the node 0.10.x then you must change the Promise library becuase it's not available natively. For more info check this: https://github.com/aws/aws-sdk-js/blob/266875c2e6a6ef5f9ff875e2cdaf36e10018d2da/doc-src/guide/browser-making-requests.md#support-for-promises – Pedro Carriço Jun 02 '16 at 15:26
  • Maybe this answer should mention how promise support is enabled in aws-sdk instead of just linking to an overview-ish page. Basically what's needed is `AWS.config.setPromisesDependency(Q.Promise);` to use Q, and corresponding for other promise implementations. More info at https://aws.amazon.com/blogs/developer/support-for-promises-in-the-sdk/, but please copy the content instead of just linking. – JHH Feb 28 '18 at 14:55
27

I believe calls can now be appended with .promise() to promisify the given method.

You can see it start being introduced in 2.6.12 https://github.com/aws/aws-sdk-js/blob/master/CHANGELOG.md#2612

You can see an example of it's use in AWS' blog https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/

let AWS = require('aws-sdk');
let lambda = new AWS.Lambda();

exports.handler = async (event) => {
    return await lambda.getAccountSettings().promise() ;
};
DefionsCode
  • 483
  • 4
  • 8
  • 3
    Note that this only works for some functions. From AWS Docs: "Note: This currently only works for operations that return an instance of AWS.Request." – rouan Jan 28 '19 at 06:46
25

You can use a promise library that does promisification, e.g. Bluebird.

Here is an example of how to promisify DynamoDB.

var Promise = require("bluebird");

var AWS = require('aws-sdk');
var dynamoDbConfig = {
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  region: process.env.AWS_REGION
};
var dynamoDb = new AWS.DynamoDB(dynamoDbConfig);
Promise.promisifyAll(Object.getPrototypeOf(dynamoDb));

Not you can add Async to any method to get the promisified version.

Martin Kretz
  • 1,523
  • 2
  • 13
  • 20
  • 2
    worked great for me! i am using the new DynamoDB-doc lib. var docClient = new doc.DynamoDB(); Promise.promisifyAll(Object.getPrototypeOf(docClient)); – Shimon Tolts Mar 05 '15 at 14:39
15

Way overdue, but there is a aws-sdk-promise npm module that simplifies this.

This just adds a promise() function which can be used like this:

ddb.getItem(params).promise().then(function(req) {
    var x = req.data.Item.someField;
});

EDIT: It's been a few years since I wrote this answer, but since it seems to be getting up-votes lately, I thought I'd update it: aws-sdk-promise is deprecated, and newer (as in, the last couple of years) versions of aws-sdk includes built-in promise support. The promise implementation to use can be configured through config.setPromisesDependency().

For example, to have aws-sdk return Q promises, the following configuration can be used:

const AWS = require('aws-sdk')
const Q = require('q')

AWS.config.setPromisesDependency(Q.Promise)

The promise() function will then return Q promises directly (when using aws-sdk-promise, you had to wrap each returned promise manually, e.g. with Q(...) to get Q promises).

JHH
  • 8,567
  • 8
  • 47
  • 91
  • AWS Version 3 allows for modular use of AWS libraries, as opposed to required the whole aws-sdk. Is there a way to set the promise dependency of say, DynamoDBClient to a third party Promise when there is no global AWS config? – ORcoder Feb 19 '21 at 02:21
7

With async/await I found the following approach to be pretty clean and fixed that same issue for me for DynamoDB. This works with ElastiCache Redis as well. Doesn't require anything that doesn't come with the default lambda image.

const {promisify} = require('util');
const AWS = require("aws-sdk");
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const dynamoDBGetAsync = promisify(dynamoDB.get).bind(dynamoDB);

exports.handler = async (event) => {
  let userId="123";
  let params =     {
      TableName: "mytable",
      Key:{
          "PK": "user-"+userId,
          "SK": "user-perms-"+userId
      }
  };

  console.log("Getting user permissions from DynamoDB for " + userId + " with parms=" + JSON.stringify(params));
  let result= await dynamoDBGetAsync(params);
  console.log("Got value: " + JSON.stringify(result));
}
C.J.
  • 81
  • 1
  • 4
  • 2
    This worked great for me with `AWS.CognitoIdentityServiceProvider`. Adding `bind` fixed the error `TypeError: this.makeRequest is not a function`. – likesalmon May 24 '19 at 21:41
  • 1
    Thanks! You are correct, I've used this approach for every AWS SDK service successfully thus far, just copy, paste, refactor! I like to wrap them into mini-libraries to maximize the readability of the main index.js code in Lambda as well. – C.J. May 27 '19 at 13:17
5

Folks, I've not been able to use the Promise.promisifyAll(Object.getPrototypeOf(dynamoDb));

However, the following worked for me:

this.DYNAMO = Promise.promisifyAll(new AWS.DynamoDB());
...
return this.DYNAMO.listTablesAsync().then(function (tables) {
    return tables;
});

or

var AWS = require('aws-sdk');
var S3 = Promise.promisifyAll(new AWS.S3());

return S3.putObjectAsync(params);
rynop
  • 50,086
  • 26
  • 101
  • 112
Cmag
  • 14,946
  • 25
  • 89
  • 140
  • 3
    You don't want to call `Promise.promisifyAll` as a constructor. Please omit the first `new` – Bergi Aug 06 '15 at 17:07
1

CascadeEnergy/aws-promised

We have an always in progress npm module aws-promised which does the bluebird promisify of each client of the aws-sdk. I'm not sure it's preferable to using the aws-sdk-promise module mentioned above, but here it is.

We need contributions, we've only taken the time to promisify the clients we actually use, but there are many more to do, so please do it!

nackjicholson
  • 4,557
  • 4
  • 37
  • 35
0

This solution works best for me:

// Create a promise object
var putObjectPromise = s3.putObject({Bucket: 'bucket', Key: 'key'}).promise(); 

// If successful, do this:
putObjectPromise.then(function(data) {
 console.log('PutObject succeeded'); })

// If the promise failed, catch the error:
.catch(function(err) { 
console.log(err); });
Ojasvi Monga
  • 4,437
  • 2
  • 18
  • 35