-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Hello,
In graphql-yoga it is common to have a field in a type that is resolved with a separated function, so the main resolver will have to return the object with those fields missing for it to be fetched later, if queried. But the interfaces generated by gql2ts do not allow undefined in those fields.
I've been doing a workaround by defining the return of queries with Partial, but the problem is when those types are nested within another type, for instance, mutation response payload. I wished to use the generated interfaces as a helper in the resolvers return definition, but I can't assign Partial to INode type.
Example, in the snippet bellow, it is shown the resolvers for the type "Post", which has the field "Author" that can be fetched separately. I can define post query's return as Partial but I can't define the UserCreatePost mutation's return as IUserCreatePostPayload because I need to assign a full IPost object, making me having to define those types or just strip away the return type definition, which makes the library a bit pointless for this use case, it certainly makes IUserCreatePostPayload generation pointless
type Post implements Node {
id: ID!
_id: String!
title: String!
description: String
authorId: String!
author: User!
}
type Mutation {
UserCreatePost(input: UserCreatePostInput!): UserCreatePostPayload
}
type Query {
post(id: ID!): Post
feed(first: Int, after: String, last: Int, before: String): PostConnection
}
input UserCreatePostInput {
title: String!
description: String
clientMutationId: String
}
type UserCreatePostPayload {
error: String
post: Post
clientMutationId: String
}export const post2IPost: model2Node<Post, GQL.IPost> = (
post: Post
): Partial<GQL.IPost> => ({
_id: post._id,
id: idToGraphqlId(post._id, idPrefix),
title: post.title,
description: post.description,
authorId: post.authorId,
});
export const resolvers: ResolverMap = {
Mutation: {
UserCreatePost: async (
_,
{
input: { description, clientMutationId, title }
}: GQL.IUserCreatePostOnMutationArguments,
{ sequelize, userId }
) => {
const PostModel = sequelize.models.Post as ModelCtor<Post>;
const post = (await PostModel.create({
description,
title,
authorId: userId
})) as Post;
return {
error: null,
post: post2IPost(post),
clientMutationId: clientMutationId || null
};
}
},
Query: {
post: async (
_,
{ id: graphId }: GQL.IPostOnQueryArguments,
{ sequelize }
) => {
const PostModel = sequelize.models.Post as ModelCtor<Post>;
const id = graphqIdToId(graphId, "post");
const postA = (await PostModel.findByPk(id)) as Post;
if (!postA) {
return null;
}
return post2IPost(postA);
},
feed
},
Post: {
author: async (
parent,
_,
{ sequelize }
): Promise<Partial<GQL.IUser>> => {
const UserModel = sequelize.models.User as ModelCtor<User>;
const user = (await UserModel.findByPk(parent.authorId)) as User;
return user2IUser(user);
},
}
};So, my idea would be an option to output types like this:
interface IUserCreatePostPayload {
error: string | null;
post: IPost | null;
clientMutationId: string | null;
}With an optional "shallow" type like this
interface IShallowUserCreatePostPayload {
error: string | null;
post: IPost | null | Partial<IPost>;
clientMutationId: string | null;
}They could be named as shallow interfaces and be generated side by side with the others so it won't break other ppl's code