Avoid writing converters #3086
-
My team uses gqlgen for building our GraphQL server. The backend services expose their APIs using gRPC. What we are finding is that our GraphQL server is littered with converters for converting gRPC responses to gqlgen generated types – this, in spite of the fact that both objects map 1-to-1. Is there a better way? I created a simple example to illustrate the problem. The backend service defines a Movie object using protobuf: message Movie {
string id = 1;
string name = 2;
string description = 3;
repeated CastMember cast = 4;
Certificate certificate = 5;
repeated string genres = 6;
Image image = 7;
int32 rank = 8;
RatingsSummary ratings_summary = 9;
int32 release_year = 10;
int32 runtime = 11;
string tagline = 12;
} The GraphQL gateway defines the same Movie object using a GraphQL schema: type Movie {
id: ID!
name: String!
description: String!
cast: [CastMember!]!
certificate: Certificate!
genres: [String!]!
image: Image
rank: Int!
ratingsSummary: MovieRatingsSummary!
releaseYear: Int!
runtime: Int!
tagline: String
} While the objects are essentially the same, the movie resolver has to convert the gRPC response to the GraphQL model: // Map the gRPC response to the GraphQL model
movies := make([]*model.Movie, len(res.Movies))
for i, movie := range res.Movies {
movies[i] = &model.Movie{
ID: movie.Id,
Name: movie.Name,
Cast: convertCastMembers(movie.Cast),
Certificate: convertCertificate(movie.Certificate),
Genres: movie.Genres,
Rank: int(movie.Rank),
RatingsSummary: &model.MovieRatingsSummary{
AggregateRating: movie.RatingsSummary.AggregateRating,
VoteCount: int(movie.RatingsSummary.VoteCount),
},
ReleaseYear: int(movie.ReleaseYear),
}
} Not only that, there is additional pain to convert enums: func convertCertificate(certificate moviev1.Certificate) model.Certificate {
switch certificate {
case moviev1.Certificate_CERTIFICATE_UNSPECIFIED:
return model.CertificateCertificateUnspecified
case moviev1.Certificate_CERTIFICATE_G:
return model.CertificateCertificateG
case moviev1.Certificate_CERTIFICATE_NR:
return model.CertificateCertificateNr
case moviev1.Certificate_CERTIFICATE_PG_13:
return model.CertificateCertificatePg13
case moviev1.Certificate_CERTIFICATE_PG:
return model.CertificateCertificatePg
case moviev1.Certificate_CERTIFICATE_R:
return model.CertificateCertificateR
default:
return model.CertificateCertificateUnspecified
}
} Is there a better workflow that I am not aware of? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Please help. Any insights on this? |
Beta Was this translation helpful? Give feedback.
-
Hi, so the same thing happens for any other codegen such as sqlc models. If the structs have identical fields, you can use Go's inbuilt struct conversion. In order to convert between mostly identical structs, you can use https://github.com/jmattheis/goverter , https://github.com/mitchellh/mapstructure or a few other libraries. |
Beta Was this translation helpful? Give feedback.
Hi, so the same thing happens for any other codegen such as sqlc models.
If the structs have identical fields, you can use Go's inbuilt struct conversion.
In order to convert between mostly identical structs, you can use https://github.com/jmattheis/goverter , https://github.com/mitchellh/mapstructure or a few other libraries.