6

I'm currently using Aws Appsync, Aws Lambda, and Aws Neptune for an application. My Lambda function uses NodeJS 12. Right now my problem is getting back the appropriate JSON format from Neptune (more specifically gremlin) for my graphql api (appsync) when I do a mutation and eventually a query (I want to make sure the mutations are working first). For example:

  1. Here is my type Post for my graphql schema with addPost mutation right above it: Post schema
  2. The addPost mutation maps to this resolver: Post Mutation Resolver
  3. Which then runs this piece of code: Lambda Code

When I run this test query to add a post, I get the following error with data being null: addPost test query and result

Does adding a vertex in gremlin return data/an object? If so, how do I get the appropriate JSON format for my appsync graphql api? I've been reading Practical Gremlin and searching the web but no luck. Thank you in advance.

MCam
  • 79
  • 5

1 Answers1

4

More than likely what you're seeing is related to an incompatibility in Lambda with the default return format for the Node.js GLV. The default format returned is GraphSONV3, which is JSON-like but not well-formatted JSON. Lambda is expecting well-formatted JSON. You can change the mimetype when establishing your connection to Neptune to use GraphSONV2 which Lambda shouldn't have any issues with.

const dc = new DriverRemoteConnection(
  `wss://<neptune-endpoint>:8182/gremlin`,
  { mimeType: "application/vnd.gremlin-v2.0+json" } 
);

The other thing to validate is how you are resolving the promise being returned by the await'd Gremlin query. It looks like in your sample code that you are taking the result of the await'd query and feeding that into JSON.stringify(). I don't think that will work, as that will effectively return the JSON stringified version of the Promise (which is what you're seeing). What you can do in this case (if you want to submit queries asynchronously) is to take your Gremlin queries (or maybe even this larger case statement) and put it into an async function outside of the Lambda handler. Example:

const gremlin = require('gremlin');
const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;
const Graph = gremlin.structure.Graph;

const dc = new DriverRemoteConnection('wss://<neptune-endpoint>:8182/gremlin',
    { mimeType: "application/vnd.gremlin-v2.0+json" }
    );

const graph = new Graph();
const g = graph.traversal().withRemote(dc);

async function callNeptune() {
    const result = await g.addV('Post').property('userId','someid').
        property('genre','somegenre').
        property('caption','somecaption').
        property('timestamp','sometimestamp').
        toList();
    console.log(result);
    dc.close();
    try {
        return result;
    } catch (error) {
        return error;
    }
}

exports.handler = async (event) => {

    const rawOutput = await callNeptune();
    const jsonOutput = JSON.stringify(rawOutput);
    const response = {
        statusCode: 200,
        body: jsonOutput,
    };
    return response;
};

In this scenario, you're await'ing the async function with your Gremlin query via an await call that is now in the handler. So you can then take the results from that and feed it into JSON.Stringify() and return it. The Lambda service will resolve the promise of the handler at that point.

FWIW, there's little benefit to using async/await from a Lambda-backed API layer to Neptune. Both the Lambda function and the Neptune server side threads will be waiting (and holding up resources) until all of the promises are resolved. In many cases this can just add complexity over just using synchronous calls. It would be different if you were doing this from a long-running containerized application or from a web-based front end, where letting other processes in the meantime makes sense.

Taylor Riggan
  • 1,963
  • 6
  • 12
  • I'm now getting "null" as a response still. When I add .toString to the end of the traversal, I get a response of "[object Promise]". – MCam Apr 17 '20 at 05:29
  • I edited my response to include some additional details around using async/await with the Gremlin GLV within a Lambda function. I was able to reproduce your issue by attempting to run your example code all within the Lambda handler. By splitting all the Gremlin queries to another async function, I was able to resolve the issue with JSON.stringify(result) returning the JSON representation of the Promise. – Taylor Riggan Apr 17 '20 at 17:26
  • I copied your code and it is still returning "null" for me. I tried running this query: **const result = await g.V().hasLabel('Post').toList();**, assuming that the Post vertex was added and I was given null as well. Even tried just returning the rawOutput as the value for body but was also given null. – MCam Apr 17 '20 at 19:51
  • Also, when I remove **.toList()** from the end of the query I get this error: **"errorMessage": "Converting circular structure to JSON\n --> starting at object with constructor 'GraphSON2Reader'\n | property '_deserializers' -> object with constructor 'Object'\n | property 'g:Traverser' -> object with constructor 'TraverserSerializer'\n --- property 'reader' closes the circle",** – MCam Apr 17 '20 at 20:12
  • Is the Lambda function configured to run in the same VPC that Neptune is hosted within? Is the Neptune cluster's security group set to allow connections from any of the possible IP addresses that the Lambda function could use (the defined subnets in the Lambda function's VPC)? – Taylor Riggan Apr 18 '20 at 00:28
  • Yes, the lambda function is in the same VPC as the Neptune Cluster. The Neptune Cluster's Security Group has an Inbound Rule that only allows TCP connections from the Lambda function's security group (the lambda function and the Neptune cluster are not in the same security group). The Lambda's security group and the Neptune Cluster's security group both have Outbound rules that allow All Types of traffic, All types of Protocols and All types of Port ranges. I set it this way according to the AWS Neptune documentation. I hope that answers your question. – MCam Apr 18 '20 at 02:03