graphql-rust / graphql-client

Typed, correct GraphQL requests and responses in Rust
Apache License 2.0
1.14k stars 157 forks source link

Graphql insert mutation: omitting fields with default values #479

Open andreaiaia opened 6 months ago

andreaiaia commented 6 months ago

Hello! I've encountered an issue with the crate: the type generated by the crate from the schema doesn't allow me to omit fields that have a default value generated by the database (for example, a serial id or creation and update timestamps), but it requires me to provide values for them. If I try to give a value of None, Rust sends a query populating the fields as null, and the database returns an error because the fields are not nullable (and I don't want to insert null values anyway).

The following example replicates the mutation I tried to execute using two mock tables that mimic the behavior of the ones I need to manage:

create table hobbies (
    id serial primary key,
    creation_timestamp timestamp with time zone not null default now(),
    username text not null,
    type text not null,
);

I want to run this mutation with Hasura GraphQL:

mutation InsertData(
    $hobbies: [hobbies_insert_input!]!
) {
    insert_hobbies(
        objects: $hobbies
    ) {
        affected_rows
    }
}

With the following input:

{
    "hobbies": [
        {
            "username": "user1",
            "type": "reading"
        },
        {
            "username": "user2",
            "type": "gaming"
        }
    ]
}

Where hobbies_insert_input is defined in the auto-generated schema that I added to the repository files using graphqurl.

When I execute this through Rust, however, I encounter the following problem: the schema forces the type used in Rust to require that I provide a value for all fields in the row, including id, creation_timestamp, and update_timestamp. If I set these fields to None, rust translates them to null, and the database gives me an error because the columns cannot be null. If I omit those values, the compiler does not compile, and manually providing a value when those fields are populated by default by the database doesn't seem like a very elegant solution.

For now the only solution I found was to create a separate module for this mutation and manually create the schema that this module uses.

justro commented 3 weeks ago

Been a while since this issue was opened, but FWIW a possible workaround is to use the "skip_serializing_none" flag on queries that may have None values that you don't want to send in the mutation. This more or less works for my case.

It would still not help for situations where either Null or Unspecified could be valid values of a field. It's probably be good to have something like an enum SpecifiableOption which has Some, None, and Unspecified as possible values, so we could differentiate in updates whether we intend to not provide a new value for a field, or to actually set the field to null.