middy: Using an async function with middy without callback throws error.

Summary Using async / await within the lambda handler function throws an error when not passing a callback into the midi constructor function. AWS documentation specifies that we don’t need to pass a callback when using async / await

https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/

Example Code index.js

const middy = require('middy')

const handler = middy(async (event, context) => {
  return {foo: 'bar'}
})

handler('asd', 'asd')

Running this throws an error:

yarn start
yarn run v1.7.0
$ node index.js
(node:25665) UnhandledPromiseRejectionWarning: TypeError: callback is not a function
    at terminate (/Users/mellisdesigns/Documents/Boilerplates/lambda-node-boilerplate/node_modules/middy/src/middy.js:151:16)
    at runNext (/Users/mellisdesigns/Documents/Boilerplates/lambda-node-boilerplate/node_modules/middy/src/middy.js:125:14)
    at runErrorMiddlewares (/Users/mellisdesigns/Documents/Boilerplates/lambda-node-boilerplate/node_modules/middy/src/middy.js:129:3)
    at errorHandler (/Users/mellisdesigns/Documents/Boilerplates/lambda-node-boilerplate/node_modules/middy/src/middy.js:159:14)
    at onHandlerError (/Users/mellisdesigns/Documents/Boilerplates/lambda-node-boilerplate/node_modules/middy/src/middy.js:167:16)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:182:7)
    at Function.Module.runMain (internal/modules/cjs/loader.js:697:11)
    at startup (internal/bootstrap/node.js:201:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:516:3)
(node:25665) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:25665) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
✨  Done in 0.15s.

Runtime Version

System:
    OS: macOS High Sierra 10.13.4
    CPU: x64 Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz
  Binaries:
    Node: 9.11.1 - ~/.nvm/versions/node/v9.11.1/bin/node
    Yarn: 1.7.0 - /usr/local/bin/yarn
    npm: 6.1.0-next.0 - ~/.nvm/versions/node/v9.11.1/bin/npm

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 9
  • Comments: 16 (4 by maintainers)

Most upvoted comments

async handler(event, context) {
  return 'Hello';
}

handler(event, context); // -> Promise
middy(handler)(event, context) // -> undefined

@Kjir I confirmed my understanding of the issue similarly. In order to test my lambda’s I did something like this for now:

const promisify = async (handler, event, context) => {
  return new Promise((resolve, reject) => {
    handler(event, context, (err, response) => {
      if (err) {
        reject(err)
      } else {
        resolve(response)
      }
    })
  })
}
it("responds with a 422 if dsafsdafasf is not specified", async () => {
    const resp = await promisify(create, { body: JSON.stringify({}) })
    expect(resp.statusCode).toEqual(422)
  })

not wild about it, but it works. It would be great to have await wait for all middlewares to flush and return a promise. So that a standard await handler(event, context) invocation works as expected.

I am also getting “TypeError: callback is not a function” when attempting to test my handlers using Middy. Is there a simpler solution as opposed to the one described above? Thanks.

Hi there, I just took a look at the PR #199 as I now only work with async handler and saw that the issue was not yet resolved. I’m going to start working on a possible fix and will keep you updated. To give you an overview, if we want to cover all the possible cases of an invoke, we should handle the following calls:

function invokeHandlerWithContext (event, context) {
  // return context.fail(new Error('failure'))
  context.succeed('success')
}

function invokeHandlerWithCallback (event, context, callback) {
  // return callback(new Error('failure'))
  callback(null, 'success')
}

async function invokeAsyncHandler (event, context) {
  // throw new Error('failure')
  return 'success'
}

You might make use of util.promisify (Requires node >= v8.0.0)