8000 Add support for semantic nullability with `@semanticNonNull` · Issue #1605 · async-graphql/async-graphql · GitHub
[go: up one dir, main page]

Skip to content
Add support for semantic nullability with @semanticNonNull #1605
@XiNiHa

Description

@XiNiHa

Description of the feature

Recently, many GraphQL frameworks and libraries are adopting semantic nullability support with @semanticNonNull, namely Relay and Apollo Kotlin at the client, Caliban and Grats at the server. There are also tools like graphql-sock and graphql-toe to help existing libraries work with semantic nullability. While it's already possible to mimic the functionality in async-graphql by manually injecting the directives, since it's a huge pain putting them everywhere and making everything optional, adding macro support for the feature in async-graphql would be very helpful.

Code example (if possible)

TODO: how would we make the feature opt-in? A Cargo feature? An attribute on macros?

struct MyObject;

#[Object]
impl MyObject {
    async fn value(&self) -> i32 {
        42
    }

    async fn optional_value(&self) -> Option<i32> {
        Some(42)
    }

    async fn fallible_value(&self, input: String) -> Result<i32> {
        Ok(input
            .parse()
            .map_err(|err: ParseIntError| err.extend_with(|_, e| e.set("code", 400)))?)
    }

    async fn fallible_optional_value(&self, input: Option<String>) -> Result<Option<i32>> {
        match input {
            Some(input) => Ok(input
                .parse()
                .map_err(|err: ParseIntError| err.extend_with(|_, e| e.set("code", 400)))?),
            None => Ok(None)
        }
    }
}

This should result in the following schema:

type MyObject {
    # infallible values as strict non-null (no change)
    value: Int!
    # optional values as nullable (no change)
    optionalValue: Int
    # fallible values as semantic non-null, marked with a directive (new!)
    fallibleValue: Int @semanticNonNull
    # fallible optional value as nullable (no change)
    fallibleOptionalValue: Int
}

In addition, Result<Vec<T>>, Vec<Result<T>>, and Result<Vec<Result<T>>> should be handled using the levels argument in the directive.

Result<Vec<T>> => [T!] @semanticNonNull(levels: [0])
Vec<Result<T>> => [T]! @semanticNonNull(levels: [1])
Result<Vec<Result<T>>> => [T] @semanticNonNull(levels: [0, 1])

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0