'How to return json from callback function within the Lambda?

I'm trying to return the login status from the Cognito callback function, which is written in the NodeJS Lambda. However when I call the API the response keep loading and I'm getting warning error.

Here is my code:

'use strict';

global.fetch = require('node-fetch');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');

module.exports.hello = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify({
      message: "Hello there"
    }),
  };

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};


module.exports.register = async (event, context, callback) => {

  let poolData =  {
      UserPoolId : 'xxxxx', // Your user pool id here
      ClientId : 'xxxxxxx' // Your client id here
  } // the user Pool Data

  let userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);

  let attributeList = [];

  let dataEmail = {
      Name : 'email',
      Value : '[email protected]'
  };

  let dataName = {
      Name : 'name',
      Value : 'Jack'
  };

  var dataPhoneNumber = {
      Name : 'phone_number',
      Value : '+94234324324234' // your phone number here with +country code and no delimiters in front
  };

  let attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
  let attributeName = new AmazonCognitoIdentity.CognitoUserAttribute(dataName);
  var attributePhoneNumber = new AmazonCognitoIdentity.CognitoUserAttribute(dataPhoneNumber);

  attributeList.push(attributeEmail);
  attributeList.push(attributeName);
  attributeList.push(attributePhoneNumber);

  userPool.signUp('[email protected]', 'H1%23$4jsk', attributeList, null, function(err, result){

    let data = {};

    if (err) {
      callback(null, {
          statusCode: 500,
          body: JSON.stringify({
            status: 'FAIL',
            message: err.message
          }),
        });
      } else {
        let cognitoUser = result.user;

        callback(null, {
          statusCode: 200,
          body: JSON.stringify({
            status: 'SUCCESS',
            message: '',
            data: {
              username: cognitoUser.getUsername(),
              id: result.userSub
            }
          }),
        });
      }
  })

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};

The warning error as follows:

Serverless: Warning: handler 'register' returned a promise and also uses a callback!
This is problematic and might cause issues in your lambda.

Serverless: Warning: context.done called twice within handler 'register'!

serverless.yml

service: test-auth 
plugins:
  - serverless-offline

provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: us-east-1

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: message
          method: get
  register:
    handler: handler.register  
    events:
      - http:
          path: register
          method: post

Any help would be appreciated, Thanks in advance.

EDIT (2019-04-01):

module.exports.register = (event, context) => {

  ...

  userPool.signUp('[email protected]', 'H1%23$4jsk', attributeList, null, function(err, result){

    // for testing purpose directly returning 
    return {
      statusCode: 500,
      body: JSON.stringify({
        status: 'FAIL',
        message: err.message
      })
    }

  })

};


Solution 1:[1]

Well the error is accurate. async wraps your return with promise. Either use callback all the way through like:

global.fetch = require('node-fetch');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');

// remove async
module.exports.register = (event, context, callback) => {

  ...

  // if you're using callback, don't use return (setup your callback to be able to handle this value as required) instead do:
  // calback({ message: 'Go Serverless v1.0! Your function executed successfully!', event })

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};

Or don't use callback, use async/await (Promise) all the way through like:

module.exports.register = async (event, context) => {

  ...

  // needs promise wrapper, when using with promise, you might want to break up your code to be more modular
  const mySignUp = (email, password, attributes, someparam) => {
    return new Promise((resolve, reject) => {
      userPool.signUp(email, password, attributes, someparam, function(err, result) {

        let data = {};

        if (err) {
          reject({
            statusCode: 500,
            body: JSON.stringify({
              status: 'FAIL',
              message: err.message
            }),
          });
        } else {
          let cognitoUser = result.user;

          resolve({
            statusCode: 200,
            body: JSON.stringify({
              status: 'SUCCESS',
              message: '',
              data: {
                username: cognitoUser.getUsername(),
                id: result.userSub
              }
            }),
          });
        }
      })
    });
  }

  // call the wrapper and return
  return await mySignUp('[email protected]', 'H1%23$4jsk', attributeList, null);

  // don't use double return
  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};

Now register will return a promise. Elsewhere in your code you can call register like:

var result = register();
result
  .then(data => console.log(data))
  // catches the reject from Promise
  .catch(err => console.error(err))

or in async/await function (Note: `await` is valid only inside `async` function)

async function someFunc() {
  try {
    var result = await register();
    // do something with result
    console.log(result);
  } catch (err) {
  // reject from Promise
    console.error(err)
  }
}

Also note use strict is not required here as node modules use strict by default.

Solution 2:[2]

Its exactly what the error message states.

All async functions return promises. module.exports.register = async (event, context, callback) => {}

You are also using the callback by calling

callback(null, {
          statusCode: 500,
          body: JSON.stringify({
            status: 'FAIL',
            message: err.message
          }),
        });

Instead of using the callback, just return the either an error or a valid response.

Solution 3:[3]

You are using an async function with a call back.

Try it this way:

Remove the callback from the async function.

async (event, context)

And modify the return as:

if (err) {
      return {
          statusCode: 500,
          body: JSON.stringify({
            status: 'FAIL',
            message: err.message
          })
        }
      }

And put an await on the function call.

Solution 4:[4]

If it helps anyone else catching this, you can add headers to the return:

return {
      statusCode: 200,
      headers: {"Content-Type": "application/json"},
      body: JSON.stringify(response.data)
    };

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Swaraj Giri
Solution 3 Praveen Kumar Palai
Solution 4 Mark