alexa-skills-kit-sdk-for-nodejs: Cannot be used with API gateway

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[X ] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Other... Please describe:

Expected Behavior

I should get a successful response from api gateway.

Current Behavior

In the simulator, I’m getting a “There was a problem with the requested skill’s response” response, and when looking at the device log, I’m seeing "invocationResponse": null,

Possible Solution

No idea

Steps to Reproduce (for bugs)

create a simple skill with a launch request handler, and wire it with an API gateway. (I used SAM)

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speechText = 'Thank you for contacting us.  How can I help you today?';

        console.log(handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .withSimpleCard(speechText)
            .getResponse());

        return handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .withSimpleCard(speechText)
            .getResponse();
    },
};

I’ve fixed the handlerInput by changing

return handlerInput.requestEnvelope.request.type === 'LaunchRequest';

to

return handlerInput.requestEnvelope.body.request.type === 'LaunchRequest';

but I cannot seem to get the response back to the alexa simulator.

What have you tried:

        return {
            statusCode: 200,
            body: JSON.stringify(handlerInput.responseBuilder
                .speak(speechText)
                .reprompt(speechText)
                .withSimpleCard(speechText)
                .getResponse())
        };  // got same outcome
     handle(handlerInput, context, callback)
     ...
        callback(null, {
            statusCode: 200,
            body: JSON.stringify(handlerInput.responseBuilder
                .speak(speechText)
                .reprompt(speechText)
                .withSimpleCard(speechText)
                .getResponse())
        }); //undefined method callback

Context

I need to be able to talk to the skill behind an API gateway.

Your Environment

  • ASK SDK for Node.js used: x.x.x “ask-sdk-core”: “^2.0.3”, “ask-sdk-model”: “^1.2.0”
  • Operating System and version: Windows 10

Node.js and NPM Info

  • Node.js version used for development: 8.9.3
  • NPM version used for development: 5.6.0

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 18 (6 by maintainers)

Most upvoted comments

HI all,

Sorry for the late reply.

  1. ResponseBuilder.getResponse only returns the response part of the full skill response. You can find the full response format here. SDK wraps the object returned from ResponseBuilder into the full response for you after the request handler returns. To put it in an example.
exports.handler = async function (event, context) {
  console.log(`REQUEST++++${JSON.stringify(event)}`);

  let responseEnvelope;

  try {
    responseEnvelope = await skill.invoke(event,context); // ==> returns full response envelope with version and session
  } catch(err) {
    console.log(`ERROR++++${JSON.stringify(err)}`);
  }

  console.log(`RESPONSE++++${JSON.stringify(responseEnvelope)}`);

  return responseEnvelope;
};
const StopAndCancelIntentRequestHandler = {
  canHandle(input) {
    const { request } = input.requestEnvelope;

    return request.type === 'IntentRequest'
      && (request.intent.name === 'AMAZON.CancelIntent' || request.intent.name === 'AMAZON.StopIntent' );
  },
  handle(input) {
    return input.responseBuilder 
      .speak('Have a nice day!')
      .getResponse();// ===> returns ResponseEnvelope.response
  }
};
  1. skill.invoke(event) is expecting the event to conform with the expect request format as shown here. If you set up the API gateway with lambda custom integration as proxy, the event passed in lambda handler will contain the full http request as shown here. In this case, you need to pass in the body of the request as it contains the actual request envelope. see below:
exports.handler = async function (event, context) {
  const responseEnvelope = await skill.invoke(JSON.parse(event.body),context);
  return {
     "body": JSON.stringify(responseEnvelope)
  };
};

Also, the api gateway expects the output of lambda to follow the format as shown here. Note that the body needs to be a string not a json object.

Regards,

No, when we say API Gateway, we mean when you setup a skill to use HTTP instead of lambda, and use the API Gateway as a lambda proxy, the “body” of the response is empty.

const skill = alexa.SkillBuilders.custom()
  .addRequestHandlers(
    ...Your Handlers
  )
  .create();


exports.handler = async function (event, context) {
  console.log(`REQUEST++++${JSON.stringify(event)}`);

  let responseEnvelope;

  try {
    responseEnvelope = await skill.invoke(JSON.parse(event.body),context);
  } catch(err) {
    console.log(`ERROR++++${JSON.stringify(err)}`);
    throw err;
  }

  console.log(`RESPONSE++++${JSON.stringify(responseEnvelope)}`);

  return {
        "body": JSON.stringify(responseEnvelope)
    };
};

Attached above is the code snippet of a working sample that uses API Gateway with Lambda proxy setting enabled.

@tianrenz @ask-sdk This is a SAM project. I had to change how we got the incoming body from the docs, which makes me think how the result is being returned isn’t formatted properly for API Gateway

index.js

const alexaApp = require('./alexa-skill')
const googleApp = require('./actions-on-google')

exports.actionsOnGoogle = googleApp.actionsOnGoogle
exports.aws = alexaApp.skillBuilder

alexa-skill.js

const Alexa = require('ask-sdk-core');
const responses = require('./responses');
const skillBuilder = Alexa.SkillBuilders.custom();
const alexaUtils = require('./alexa-util')

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        requestBody = JSON.parse(handlerInput.requestEnvelope.body);
        return requestBody.request.type === 'LaunchRequest')
    },
    handle(handlerInput) {
        const speechText = 'Thank you for contacting us.  How can we help you today?';

        console.log(handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .withSimpleCard(speechText)
            .getResponse());

        return handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .withSimpleCard(speechText)
            .getResponse();
    },
};

assistant.yml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
  Function:
    Runtime: nodejs6.10
    Timeout: 180
Resources:
  Alexa:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.aws
      CodeUri: .
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /alexa
            Method: post
  ActionsOnGoogle:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.actionsOnGoogle
      CodeUri: .
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /actionsOnGoogle
            Method: post

That doesn’t help me. The return from the handler is an invalid format for api gateway. Just put the lambda behind an api gateway and hit it from the Alexa skill builder “test” and you’ll see what issues I’m having.

On Fri, May 11, 2018, 5:27 PM Tianren Zhang notifications@github.com wrote:

Hi @westlakem https://github.com/westlakem ,

the response returned from the handler contains only the response part of the body. (see here https://developer.amazon.com/fr/docs/custom-skills/request-and-response-json-reference.html). The SDK will fill in the rest for you (version, sessionAttributes, etc.)

If you need the access to the full response body, you can see the example here https://developer.amazon.com/fr/docs/custom-skills/request-and-response-json-reference.html .

const responseEnvelope = await skill.invoke(event, context);

Then you can wrap the responseEnvelope into your lambda response body.

Regards,

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/381#issuecomment-388490140, or mute the thread https://github.com/notifications/unsubscribe-auth/AES98jx3V4yHTUPwhbQ0uT42aObsSp8aks5txgI0gaJpZM4T8BUn .