aws / aws-appsync-community

The AWS AppSync community
https://aws.amazon.com/appsync
Apache License 2.0
506 stars 32 forks source link

AppSync JavaScript Resolvers: No difference between undefined (void 0) and null #305

Open MarcusCramer91 opened 1 year ago

MarcusCramer91 commented 1 year ago

Null and undefined in mutations have quite the different meaning, e.g.

  1. Setting a field to null means removing the field from the database record.
  2. Leaving a field undefined means not altering a database record.

In AppSync JS resolvers however, the following check is true (and is of course false for regular JS): void 0 === null

So given an AppSync input like { fieldA: 'foo', fieldB: null } the only way to check if a field is null or not passed at all is via Object.keys(input).includes(field)

That was quite the surprise for me.

Sorry if this is a repost or I missed it in the docs, it seems so basic to me that surely someone must have stumbled across this?

jbschooley commented 3 months ago

Yep, just found this out today. null === undefined also returns true. Not sure how I didn't notice before, but I'm now needing to change a large portion of my code simply to detect differences between null and undefined inputs.

timheilman commented 3 months ago

This just bit me too. The point of the === operator in javascript is that null === undefined is false. But not for the javascript runtime in appsync. How have people worked around this?

hackmajoris commented 3 months ago

This just bit me too. The point of the === operator in javascript is that null === undefined is false. But not for the javascript runtime in appsync. How have people worked around this?

try use this approach if it applies to your needs

if (Object.keys(input).includes('some_attr') {
   do_something
}
hackmajoris commented 3 months ago

AWS answer on this:

  1. This behavior is expected within our runtime. We do not distinguish between null and undefined.

  2. To determine if a property exists on an object, you can use the Object.hasOwn() function. This can be used to check for undefined value

jbschooley commented 3 months ago

That is really something that should be fixed. That and the Object.keys workaround do work, but they don't work well for workflows like this:

ctx.args.input = {
    id: "abc"
}

const subset = {
    color: ctx.args.input.color
}

subset.color should be undefined and if I am, for example, updating a database, should be ignored. Instead it's null and would nullify the value of color in the database.

This can be worked around but is quite frustrating to do when you've been writing JavaScript for a decade and have developed habits based on how every other runtime interprets the language.

hackmajoris commented 3 months ago

@jbschooley

That is really something that should be fixed. That and the Object.keys workaround do work, but they don't work well for workflows like this:

ctx.args.input = {
    id: "abc"
}

const subset = {
    color: ctx.args.input.color
}

subset.color should be undefined and if I am, for example, updating a database, should be ignored. Instead it's null and would nullify the value of color in the database.

This can be worked around but is quite frustrating to do when you've been writing JavaScript for a decade and have developed habits based on how every other runtime interprets the language.

For your use case, you have to check every attribute, like:

tx.args.input = {
    id: "abc"
}

const inputToUpdate = {}

if (Object.hasOwn(tx.args.input, 'color')) {
     inputToUpdate.color = tx.args.input.color
}