Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Double Stringify() for AWS AppSync AWSJSON attributes #807

Open
QAnders opened this issue Oct 3, 2023 · 2 comments
Open

Double Stringify() for AWS AppSync AWSJSON attributes #807

QAnders opened this issue Oct 3, 2023 · 2 comments

Comments

@QAnders
Copy link

QAnders commented Oct 3, 2023

graphql-tag has served us well, and it's a great module, but I've been trying to figure this one out, and it seems related to graphql-tag...

We are running AWS AppSync and it's a bit finnicky with JSON data as input, so it could be some AWS thing as well...

We have an invoice object, on which we run mutations (e.g. create the invoice in the DB), and inside the invoice object we have other attributes that are JSON.

In the AppSync schema the attributes are defined as AWSJSON which will take a stringify'd JSON as input.

In the code though, calling the mutation I have to stringify it twice to get it working, like the below sample:

const query = `
    mutation createOutgoingInvoice{
      outgoinginvoice(
        input: {
          invoice_data: {
            currency: "${currency}",
            references: ${JSON.stringify(JSON.stringify(references))}
          },
          org_nr: "SE123"
        }
      ) {
        id
      }
    }
  `;

// query: gql(query)

The mutation works and the invoice is created, but there's something weird going on... Has anyone else come across this using AppSync?

@jerelmiller
Copy link
Member

jerelmiller commented Oct 3, 2023

Hey @QAnders 👋

What type is that references variable? Would you be able to share a runnable reproduction, or a more complete example that shows the behavior you're seeing? Its difficult to tell if this is because of JS strings, graphql-tag, or something else. Any more info you can provide would be super helpful.

EDIT:

I'm willing to bet this is more a property of string interpolation in JavaScript and not so much about graphql-tag behavior. JSON.stringify(...) is going to return a string, but keep in mind its an unquoted string, so your query is going to look something like this after the first JSON.stringify:

mutation createOutgoingInvoice{
  outgoinginvoice(
    input: {
      invoice_data: {
        currency: "USD",
        references: {"some": "field", "value": 2}
      },
      org_nr: "SE123"
    }
  ) {
    id
  }
}

Note how its set to the object, and again, this is because it returns an unquoted string. Since your GraphQL server expects a stringified JSON object, this is where your 2nd JSON.stringify is doing the work. Its stringifying the string, so it will add double quotes and escape all double quotes on the keys/string values.

While you can use interpolation to add variables to your query, I'd recommend that you use GraphQL variables to pass this value to your server. This allows your query to be a static string. Passing references via GraphQL variables should only require a single JSON.stringify()

mutation createOutgoingInvoice($currency: String, $references: AWSJSON) {
  outgoinginvoice(
    input: {
      invoice_data: {
        currency: $currency,
        references: $references
      },
      org_nr: "SE123"
    }
  ) {
    id
  }
}

Then in your request (not sure what library you're using to fetch, but here is the raw fetch version of it):

import { print } from 'graphql';

const mutation = gql`
  mutation createOutgoingInvoice {
    # ...
  }
`

fetch('/graphql', {
  method: 'POST',
  headers: {
    'content-type': 'application/json'
  },
  body: JSON.stringify({
    // NOTE: If you're using apollo or another library, this print is usually done for you
    query: print(mutation),
    variables: { 
      currency: 'USD',
      references: JSON.stringify(references)
    }
  })
)

See if you have better luck with this approach!

@QAnders
Copy link
Author

QAnders commented Oct 4, 2023

Thanks for getting back to me! Much appreciated!

I did a lot (I mean a lot) of testing and various attempts in getting it working and I have too tried with variables, and that's actually where I struggled the longest as that made most sense to use...
However I never got it working as it would seem AppSync rejects anything from variables, regardless of the "stringify()" number.

AppSync then returns an error like:

WrongType: argument 'input.invoice_data.bankaccount_data' with value 'StringValue{value='$bankaccount_data'}' is not a valid 'AWSJSON'

The only way (I found) to get it to accept anything in variables is to set the schema to String instead of AWSJSON but that is messing with my Lambda and I get some "weird" half parsed JSON in my Lambda.
Using AWSJSON gives me a valid JSON object in my Lambda so I can't budge from that...

I guess I just have to live with the double stringify() and hopefully this issue can help some other poor sod struggling to get request with JSON into AppSync...

I added some logging to AppSync, and here we can clearly see that your point, @jerelmiller , is totally accurate on a working query:

GraphQL Query: mutation createOutgoingInvoice {
  outgoinginvoice(
    input: {invoice_data: {currency: "SEK", bankaccount_data: "[{\"bic\":null,\"iban\":null,\"account_name\":\"Bankgiro\",\"account_number\":\"... cut ...\"}]"

bankaccount_data is coming in as a "String" (as it is inclosed in "") and very clearly it is "double" stringify()'d.

Trying the same with variables I get this on a "double" stringify:

"bankaccount_data": "\"[{\\\"bic\\\":null,\\\"iban\\\":null,\\\"account_name\\\":\\\"Bankgiro\\\",\\\"account_number\\\":\\\... cut ...\\\"}]\""

Using only one stringify on the variables gives me the expected "string" in the logs, but I still get the AWSJSON error from AppSync:

"bankaccount_data": "[{\"bic\":null,\"iban\":null,\"account_name\":\"Bankgiro\",\"account_number\":\"... cut ...\"}]"

So, I can conclude, that something in AppSync and how it move the values from variables to the mutation query is messing it up and the only solution I can find is to have the JSON objects doubly stringify'd and in the mutation query itself...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants