Typescript typing if separating out the resolver impl #165
-
I was unable to figure out how to appropriately apply typescript typing if I wanted to separate the resolver into its own function. It seems the typing is very tied to what you define in the This results in a giant file for me.
What's the best way to type out a resolver (or even the When I looked into the TS defs for the field / resolver, they seem to contain a lot of generics that I may not care about defining, or even know how to define. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
So if you are defining your resolver outside the field, you can't benefit from inference and need to manually type your resolver. I'm on a phone, so examples here will be a bit simplified and probably have some errors or typos.. There are 2 general options. The first option is to define functions that load the required data (or perform the required action) and have that function take whatever args it needs, rather than following the resolver convention. This let's you extract your core logic in a way that is abstracted from graphql, and might be re-usable. builder.queryType({
fields: (t) => ({
user: t.field({
type: User,
args: {
id: t.arg.id(),
},
resolve: (parent, args) => loadUserById(args.id)
}),
});
async function loadUserById(id: string | number | null) {
if (id == null) return null
// Load user here
} If your resolver is complex you can use methods like builder.queryField or builder.objectField to separate those fields out into their own files. builder.queryField('user', (t) => t.field({
type: User,
args: {
id: t.arg.id(),
},
resolve: async (parent, args, context) => {
if (!args.id) {
return null;
}
return context.dataSources.users.getUserById(args.id as number);
},
}); I would almost always do one of the above. Generally you don't want to manually type your resolvers, and writing resolvers outside the context of a field is a bit of an anti-pattern. Manually typing out the resolver function is possible: builder.queryType({
fields: (t) => ({
user: t.field({
type: User,
args: {
id: t.arg.id(),
},
resolve: userResolver
}),
});
async function userResolver(parent: {}, args: { id: number | string | null | undefined}, context GQLContext): Promise<User> {
// Resolve user
} This almost never worth it. It's a pain to type, and you lose most of the benefits of using GiraphQL. If you do go this route, nullable args can be either null or undefined, and by default IDs can be string or number (the types can be changed, but by unless you also add validation to your server using just string or number might not be safe. If you need to go this route making a generic type helper type for resolvers would probably be a good idea. |
Beta Was this translation helpful? Give feedback.
So if you are defining your resolver outside the field, you can't benefit from inference and need to manually type your resolver. I'm on a phone, so examples here will be a bit simplified and probably have some errors or typos..
There are 2 general options. The first option is to define functions that load the required data (or perform the required action) and have that function take whatever args it needs, rather than following the resolver convention. This let's you extract your core logic in a way that is abstracted from graphql, and might be re-usable.