glocore
glocore

Reputation: 2164

Invoke Java Lambda function from Node.js

I have a Lambda function written in Java, which I would like to invoke from NodeJS. Is this possible? I get the following error:

An error occurred during JSON parsing: java.lang.RuntimeException
java.lang.RuntimeException: An error occurred during JSON parsing
Caused by: java.io.UncheckedIOException: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: lambdainternal.util.NativeMemoryAsInputStream@5dc0ff7d; line: 1, column: 1]
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: lambdainternal.util.NativeMemoryAsInputStream@5dc0ff7d; line: 1, column: 1]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:857)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:62)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1511)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1102)

Here's the code for my Lambda:

public String handleRequest(String input, Context context) {
  context.getLogger().log("input: " + input + "\n");
  JSONObject obj = new JSONObject(input);
  String dest_key = obj.getString("key");
  context.getLogger().log("key: " + dest_key + "\n");

  ...
}

And here's my JavaScript invoking the above Lambda:

const AWS = require('aws-sdk');

const payload = "{\"key\": \"slide.pptx\"}"

AWS.config.loadFromPath('./config.json');
const lambda = new AWS.Lambda({ region: "ap-south-1" });
const params = {
  FunctionName : 'slide-builder',
  InvocationType : 'RequestResponse',
  Payload: payload // I get the same error even without a payload
};

lambda.invoke(params, function(err, data) {
  if (err) console.log(err, err.stack);
  else console.log(JSON.stringify(data));
});

Upvotes: 1

Views: 1106

Answers (2)

glocore
glocore

Reputation: 2164

(From this answer)

public class LambdaFunctionHandler implements RequestHandler<Map<String,Object>, String> {
    public String handleRequest(Map<String,Object> input, Context context) {
        context.getLogger().log(input.get("key"));
        ...
    }
}

I didn't have much luck with stdunbar's solution, but this worked for me.

Upvotes: 1

stdunbar
stdunbar

Reputation: 17535

You have two options that will work better than what you have. Trying to get a String has never worked for me as the Java Lambda tries to interpret the JSON payload before you get it.

One option is to eliminate the Lambda trying to do any interpretation on your input object. Something like:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public void handleRequest(InputStream inputStream,
                          OutputStream outputStream,
                          Context context) throws IOException {

    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode jsonNode = objectMapper.readTree(inputStream);
    String key= jsonNode.get("key").asText();
    // key will now be "slide.pptx"

    // create object to return...
    MyObject myObject = new MyObject();

    // create JSON string
    String jsonReturn = objectMapper.writeValueAsString(myObject);

    // "return" the string
    outputStream.write(jsonReturn .getBytes(Charset.forName("UTF-8")));
}

What you've done is avoided the Java Lambda "helpfulness" when it tries to make the incoming data into the object you want. That works pretty well when you have a full object but doesn't do what you want here.

Another option is to create an object that mirrors your Node object:

public class KeyObject {
    private String key;

    public String getKey() {
        return key;
    }
}

and then have your handler function be:

public String handleRequest(KeyObject key, Context context) {

    String fileName = key.getKey();

    // return as you're doing now.
}

I believe either of these will do what you want.

Upvotes: 1

Related Questions