Rust Async Graphql Json InputObject Type

252 views Asked by At

I'm trying to use Async Graphql, I want to use sqlx json type in model. In normal api operations the code is running. But when I want to use the async graphql InputObject macro, I get an error. The codes I used are as follows, I couldn't find a solution for the problem.

#[derive(Serialize, Deserialize, Debug, Clone, sqlx::Type)]
#[sqlx(transparent)]
pub struct Meta(Map<String, Value>);

scalar!(Meta);

#[derive(Debug, Serialize, Deserialize, Clone, FromRow, SimpleObject)]
pub struct User {
    #[serde(skip_serializing)]
    #[graphql(skip)]
    pub id: Uuid,
    pub name: String,
    pub email: String,
    #[serde(skip_serializing)]
    #[graphql(skip)]
    pub password: String,
    #[serde(skip_serializing)]
    #[graphql(skip)]
    pub meta: Json<Meta>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

No problem so far. But the code below gives an error.

#[derive(Debug, Deserialize, Validate, InputObject)]
pub struct RegisterInput {
    #[validate(length(min = 4, max = 10))]
    pub name: String,
    #[validate(email)]
    pub email: String,
    #[validate(length(min = 6))]
    pub password: String,
    pub meta: Json<Meta>,
}

InputObject error.

error[E0277]: the trait bound `sqlx::types::Json<Meta>: InputType` is not satisfied
  --> src/dto.rs:13:40
   |
13 | #[derive(Debug, Deserialize, Validate, InputObject)]
   |                                        ^^^^^^^^^^^ the trait `InputType` is not implemented for `sqlx::types::Json<Meta>`
   |
   = help: the following other types implement trait `InputType`:
             Arc<T>
             Arc<[T]>
             Arc<str>
             BTreeMap<K, V>
             BTreeSet<T>
             Box<T>
             Box<[T]>
             Box<str>
           and 49 others
   = note: this error originates in the derive macro `InputObject` (in Nightly builds, run with -Z macro-backtrace for more info)

Can you help on the subject?

1

There are 1 answers

0
xamgore On

The problem is that sqlx::types::Json is not an InputObject, which is why you can't convert it from a query. The other issue is that it must be displayed in the GraphQL schema somehow.

Try defining a wrapper struct like JSON(T), make it a GraphQL scalar type to bring InputObject.

struct JSON<T>(pub T);

impl<T> Serialize for JSON<T>
where
  T: Serialize + DeserializeOwned,
{
  fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
    self.0.serialize(serializer)
  }
}

impl<'de, T> Deserialize<'de> for JSON<T>
where
  T: Sized + Serialize + DeserializeOwned,
{
  fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
    let inner: T = serde::de::Deserialize::deserialize(deserializer)?;
    Ok(Self(inner))
  }
}

It just takes any input string, and deserializes it from a JSON-string into T. In GraphQL schema there will be a type JSON, which is a string. To register it we write:

async_graphql::scalar!(JSON<sqlx::types::Json<Meta>>, "JSON");

#[derive(..., InputObject)]
pub struct User {
  // ...
  pub meta: JSON<sqlx::types::Json<Meta>>,
}

And that's it.