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

Final solution for the eternal null VS undefined problem #1416

Open
frederikhors opened this issue Dec 12, 2020 · 8 comments
Open

Final solution for the eternal null VS undefined problem #1416

frederikhors opened this issue Dec 12, 2020 · 8 comments

Comments

@frederikhors
Copy link
Collaborator

I think this topic could win the award as the most debated topic on web all along
(since that damn day null was invented, at least 😄)!

I would like to summarize here everything I have read and tried on the subject, in the hope that Santa Claus will give us the robust solution we deserve this year (at least for gqlgen).

THE PROBLEM

As a developer I would like a quick way to identify in my resolvers if the user has deliberately chosen null as the value of the field (string/int/time/...) or if he is using sparse updates and has omitted those fields entirely.

PROPOSALS READ AROUND

  1. the official solution proposed by Gqlgen team (@vektah at least), use map[string]interface{} (https://gqlgen.com/reference/changesets/); but of course there are several counter-arguments

  2. nullable types (proposed many times, one for all here); but as @jszwedko said it seems to not work either

  3. checking ArgumentMap from context as suggested hereInput types: null vs unspecified #505 (comment); the solution I'm using although it is very verbose and I need things like code generation

  4. methods for setters proposed by the amazing @danilobuerger which is still a WIP

THREADS ON THE TOPIC

  1. Input types: null vs unspecified #505

  2. Input types: cant use methods for setters #506

  3. changesets without using reflections? #977

  4. Make Input Schema optional #866

  5. using sqlboiler, null.string fields in structs, field resolvers created #646

WE ARE NOT ALONE

  1. https://webdevstation.com/post/Work-with-nullable-strings-in-gqlgen-GraphQL-Server-0x4e28

  2. https://www.calhoun.io/how-to-determine-if-a-json-key-has-been-set-to-null-or-not-provided/

  3. Differentiate between JSON 'key = null' and 'key not set' guregu/null#39

  4. Add support for null literals graphql-go/graphql#401

  5. Support null literal graphql-go/graphql#178

  6. Support null literals as a value for arguments and input object fields datastax/cassandra-data-apis#6

  7. Support for Null Values in mutations graph-gophers/graphql-go#210

  8. Support where Null value came from graphql-rust/juniper#183

  9. Arguments cannot handling patching correctly graphql-rust/juniper#108

PLEASE

Help us, gqlgen team.

🙏

@nanozuki
Copy link

nanozuki commented Dec 18, 2020

Very glad to see your effort to deal with the problem of optional/nullable.
I vote for solution 2: Generate NullType to handle optional params/models.

  1. It's usually used in golang ecosystem, also in std packages: https://golang.org/pkg/database/sql/#NullString
  2. In graphQL schema, undefined has no difference to null. If the type is String! it can't be also null and undefined.
  3. For the motioned problem: IF a field is optional, the field value can be null, and the meaning of these two situations are different, a developer should use a special type, maybe NullNullString. (In some other language such as rust, you can see some type like Option<Option<T>>)
  4. gqlgen is an awesome code generation framework, and code generation is the best way to live with NullType before the generic type feature publish.
  5. After the generic type feature published, it's easy to migrate to generic types since no need to change the code structure.

@oiime
Copy link

oiime commented Mar 11, 2021

Here's an additional, slightly different but just as ugly solution, it uses GetFieldContext instead of GetRequestContext and gives back a list of "touched" fields within an object

// GetArgumentFieldnames returns a list of fieldnames from an argument
func (r *Resolver) GetArgumentFieldnames(ctx context.Context, name string) []string {
	fieldContext := graphql.GetFieldContext(ctx)
	names := []string{}
	for _, arg := range fieldContext.Field.Arguments {
		if arg.Name != name {
			continue
		}
		for _, child := range arg.Value.Children {
			names = append(names, child.Name)
		}
	}
	return names
}

@Panakotta00
Copy link

Is there any progress on this?

I would allow for a optional struct at least for input fields,
that allows to differentiate defined and undefined fields.

Especially now with Go 1.18's generics this could be implemented, more or less type safe.

Here a Reddit Post that discusses optionals a bit further.

@maaft
Copy link

maaft commented Jun 9, 2022

Also interested!

@sonatard
Copy link
Contributor

sonatard commented Mar 1, 2023

gqlgen supported Go 1.18 and Generics.
Supporting Optional has become a reality.

@Desuuuu Desuuuu mentioned this issue Mar 18, 2023
2 tasks
@Desuuuu
Copy link
Contributor

Desuuuu commented Mar 18, 2023

I drafted a PR to address this if anyone wants to have a look!

@StevenACoffman
Copy link
Collaborator

I would like to get more opinions on #2585 (comment) please!

@Jin5823
Copy link

Jin5823 commented Mar 19, 2024

# graphql
type Mutation {
  submit(input: SubmitInput!): SubmitPayload
}

input SubmitInput {
  firstName: String
  lastName: String
  email: String
}
// go
type SubmitInput struct {
	FirstName **string                            
	LastName  **string                            
	Email     **string                            
}

var input SubmitInput

if input.Email == nil {
	// this is undefined
}
if input.Email != nil && *input.Email == nil {
	// this is null
}
if input.Email != nil && *input.Email != nil {
	email := **input.Email
	// this is value
}

Hi !~ Can this be one of solutions ?

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

9 participants