I have following GraphQL mutation:
#[derive(Queryable, SimpleObject)]
struct OtpPhoneRequest {
id: edgedb_protocol::model::Uuid,
otp: i32,
phone: String,
confirmed_at: Option<edgedb_protocol::model::Datetime>,
}
#[Object]
impl Authentication {
async fn confirm_otp<'ctx>(&self, ctx: &Context<'ctx>, token: String, otp: i32) -> OtpPhoneRequest {
let edb = &ctx.data_unchecked::<AppContext>().edb;
let otp_request = edb
.query_single::<OtpPhoneRequest, _>(
"
select std::assert_single((
select OtpPhoneRequest {
id,
otp,
phone,
confirmedAt
} filter (OtpPhoneRequest.id = <uuid>$0)
))
",
&(edgedb_protocol::model::Uuid::from_str(token.as_str()).unwrap(),),
)
.await.unwrap();
return otp_request.unwrap()
}
}
The problem is that async-graphql handlers require to return type compatible with OutputType
, so I get an error when deriving OtpPhoneRequest
from SimpleObject
the trait bound `edgedb_protocol::model::Uuid: OutputType` is not satisfied
required for `&edgedb_protocol::model::Uuid` to implement `OutputType`
the trait bound `edgedb_protocol::model::Datetime: OutputType` is not satisfied
required for `std::option::Option<edgedb_protocol::model::Datetime>` to implement `OutputType`
I also tried to create custom scalars for edgedb types:
pub struct UuidScalar(edgedb_protocol::model::Uuid);
#[async_graphql::Scalar]
impl ScalarType for UuidScalar {
fn parse(value: async_graphql::Value) -> std::result::Result<UuidScalar, InputValueError<UuidScalar>> {
if let async_graphql::Value::String(s) = value {
let parsed = edgedb_protocol::model::Uuid::from_str(s.as_str())?;
Ok(UuidScalar(parsed))
// How to convert Uuid to UuidScalar?
} else {
Err(InputValueError::custom("Value for Uuid scalar should be String"))
}
}
fn to_value(&self) -> async_graphql::Value {
async_graphql::Value::String(self.0.to_string())
}
}
But then this scalars aren't compatible with Queryable
.
I can create two separate structs deriving from Queryable and SimpleObject and manually convert between them or implement From
trait, but, how can I avoid code duplication and use single struct?
I would not try to return or align database type to graphql response type and instead map one to another, this is not code duplication, but separation of concerns and a good practice.
Regarding uuid, you can include a feature flag which integrates
async-graphql
withuuid
crate inCargo.toml
:or implement your own scalar if you use different uuid library based on example.