Coder Social home page Coder Social logo

Comments (18)

sunli829 avatar sunli829 commented on April 28, 2024 2

Upgrade to 1.9.4 and take a look at this test.
https://github.com/sunli829/async-graphql/blob/fd156500e316e596a5ae47b40f6d99825b5990f7/tests/interface.rs#L4

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024 1

I see what you mean. You want to return a combination of two interfaces. Let me think for a moment.Because I'm not sure that GraphQL still has this functionality.

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024 1

This is really a big problem, and I'm going to solve it now. Thank you very much.๐Ÿ˜

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024

Very easy๏ผŒhere's an example, MyObj implementedIntefaceA and InterfaceB.

struct MyObj;

#[async_graphql::Object]
impl MyObj {
    #[field]
    async fn value_a(&self) -> i32 {
        unimplemented!()
    }

    #[field]
    async fn value_b(&self) -> i32 {
        unimplemented!()
    }
}

#[async_graphql::Interface(field(name = "value_a", type = "i32"))]
struct InterfaceA(MyObj);

#[async_graphql::Interface(field(name = "value_b", type = "i32"))]
struct InterfaceB(MyObj);

from async-graphql.

phated avatar phated commented on April 28, 2024

I think there might be a problem in representing that structure in a resolver.

If you implement:

pub struct QueryRoot;

#[async_graphql::Object]
impl QueryRoot {
  #[field]
  async fn my_objs(&self, _ctx: &Context<'_>) -> InterfaceA {
    (MyObj {}).into()
  }
}

In the above example, the server doesn't know about InterfaceB and the query execution fails.

I put together a sample repository at https://github.com/phated/async-graphql-interfaces that identifies the problem.

The query that needs to succeed is:

{
    myObjs {
        ... on InterfaceA {
            valueA
        }
        ... on InterfaceB {
            valueB
        }
    }
}

from async-graphql.

phated avatar phated commented on April 28, 2024

I've been thinking a lot about Interfaces in GraphQL to support Relay in my data structures and I wonder if they are better represented in Rust by implementing a trait.

What do you think of this API?

struct MyObj;

#[async_graphql::Object]
impl MyObj {
  async fn value_c(&self) -> i32 {
    3
  }
}

// Would the macro go on trait or impl?
#[async_graphql::Interface]
trait InterfaceA {
  async fn value_a(&self) -> i32;
}

// Would the macro go on trait or impl?
#[async_graphql::Interface]
trait InterfaceB {
  async fn value_b(&self) -> i32;
}

// Maybe it would go on both?
#[async_graphql::Interface]
impl InterfaceA for MyObj {
  #[field]
  async fn value_a(&self) -> i32 {
    1
  }
}

// Maybe it would go on both?
#[async_graphql::Interface]
impl InterfaceB for MyObj {
  #[field]
  async fn value_b(&self) -> i32 {
    2
  }
}

pub struct QueryRoot;

#[async_graphql::Object]
impl QueryRoot {
  #[field]
  async fn my_objs(&self, _ctx: &Context<'_>) -> impl InterfaceA + impl InterfaceB {
    (MyObj {}).into()
  }
}

I'm not sure if this is possible with Rust macros because I don't understand them enough.

(I added an sketch of this API at https://github.com/phated/async-graphql-interfaces/blob/master/src/ideal.rs)

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024

This seems like a pretty good design, but unfortunately it cannot be implemented due to the limitations of the rust procedural macros. The current interface is defined in a way that I've thought about for a long time, is easy to use and easy to understand, and more importantly is rustfmt friendly. See how juniper defines the interface. It's similar to what you want, but not easy to use.

from async-graphql.

phated avatar phated commented on April 28, 2024

Thank you, I was just going to write that. I believe it is a requirement by GraphQL spec.

This is a common pattern when using Relay because their client adds the Node fragment onto every query.

Juniper doesn't support this as far as I can tell, which was the reason I found your awesome library.

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024

Is it something like that?

interface Node {
  id: ID!
}

interface Resource implements Node {
  id: ID!
  url: String
}

interface Image implements Resource & Node {
  id: ID!
  url: String
  thumbnail: String
}

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024

Why not just return MyObj?

#[async_graphql::Object]
impl QueryRoot {
  #[field]
  async fn my_objs(&self, _ctx: &Context<'_>) -> MyObj{
    MyObj {}
  }
}

from async-graphql.

phated avatar phated commented on April 28, 2024

Each interface doesn't need to implement the others in a chain. See https://spec.graphql.org/draft/#example-ab5e5

from async-graphql.

phated avatar phated commented on April 28, 2024

MyObj isn't the only struct implementing the interface, I was trying to shrink the example.

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024

Does GraphQL support such a field type?

The type of the Query {
    myObj: InterfaceA & InterfaceB
}

from async-graphql.

phated avatar phated commented on April 28, 2024

I believe each would need to be an object that implements each interface. The interface values seem to be attached to the type declaration.

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024

Async-graphql currently does not support interface implementing interface.
Is the following function what you want?
MyObj implements InterfaceB, and because InterfaceB implements InterfaceA, MyObj automatically implements InterfaceA.

    struct MyObj;

    #[async_graphql::Object]
    impl MyObj {
        #[field]
        async fn value_a(&self) -> i32 {
            1
        }

        #[field]
        async fn value_b(&self) -> i32 {
            2
        }

        #[field]
        async fn value_c(&self) -> i32 {
            3
        }
    }

    #[async_graphql::Interface(field(name = "value_a", type = "i32"))]
    struct InterfaceA;

    #[async_graphql::Interface(field(name = "value_b", type = "i32"), implements = "InterfaceA")]
    struct InterfaceB(MyObj);

    struct Query;

    #[Object]
    impl Query {
        #[field]
        async fn my_obj(&self) -> InterfaceB {
            MyObj.into()
        }
    }

    let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
    let query = format!(
        r#"{{
            myObj {{
               ... on InterfaceA {{
                valueA
              }}
              ... on InterfaceB {{
                valueB
              }}
              ... on MyObj {{
                valueC
              }}
            }}
        }}"#
    );
    assert_eq!(
        schema.execute(&query).await.unwrap().data,
        serde_json::json!({
            "myObj": {
                "valueA": 1,
                "valueB": 2,
                "valueC": 3,
            }
        })
    );

from async-graphql.

phated avatar phated commented on April 28, 2024

I believe that would solve my current problem, yes.

from async-graphql.

phated avatar phated commented on April 28, 2024

It seems to be working! Thank you so much ๐Ÿ™‡

I will keep testing it on my project and file anything I find.

from async-graphql.

sunli829 avatar sunli829 commented on April 28, 2024

The last time the solution is not good, I think because I'm not support http://spec.graphql.org/draft/#sec-Interfaces.Interfaces-Implementing-Interfaces (Draft), but in fact is another bug.
I fixed this bug in version 1.9.8 and removed the implements attribute on the async_graphql::Interface.


Below is the updated test, which requires manual registration because InterfaceA is not directly referenced by the Schema.
https://github.com/sunli829/async-graphql/blob/73aef368dfe2eb046f6d7c012f2576e19a002724/tests/interface.rs#L98

from async-graphql.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.