Need examples for DRYer code patterns #426
-
I have made my code much DRYer (do-not-repeat-yourself-er) from the initial example, but I'm a bit stuck in trying to extract correct types from the meta values that Pothos uses. Here is what I have so far (these are across several files for organization purposes): in util/pothos: import { QueryFieldBuilder } from '@pothos/core';
import SchemaBuilder from '@pothos/core';
export type PothosT = QueryFieldBuilder<
PothosSchemaTypes.ExtendDefaultTypes<{}>,
{}
>
export const builder = new SchemaBuilder({}); in resources/root/schema: import { getPost, getPosts } from '../posts/queries';
import { builder } from '../../util/pothos';
builder.queryType({
fields: (t) => ({
post: getPost(t),
posts: getPosts(t),
}),
});
export const schema = builder.toSchema({}); in resources/posts/objects: import { builder } from '../../util/pothos';
import { User } from '../users/objects';
import { Users } from '../users/mocks';
import { Comments } from '../comments/mocks';
import { Comment } from '../comments/objects';
export interface IPost {
id: string;
authorId: string;
title: string;
content: string;
}
export const Post = builder.objectRef<IPost>('Post');
Post.implement({
fields: (t) => ({
id: t.exposeID('id'),
title: t.exposeString('title'),
content: t.exposeString('content'),
author: t.field({
type: User,
nullable: true,
resolve: (post) => [...Users.values()].find((user) => user.id === post.authorId),
}),
comments: t.field({
type: [Comment],
resolve: (post) => [...Comments.values()]
.filter((comment) => comment.postId === post.id),
}),
}),
}); in resources/posts/mocks: import faker from '@faker-js/faker';
import { FAKER_SEED } from '../../util/constants';
import { IPost } from './objects';
faker.seed(FAKER_SEED)
export const Posts = new Map<string, IPost>();
for (let i = 1; i <= 100; i += 1) {
Posts.set(String(i), {
id: String(i),
authorId: String(faker.datatype.number({ min: 1, max: 100 })),
title: faker.lorem.text(),
content: faker.lorem.paragraphs(2),
});
} in resources/posts/queries: import { DEFAULT_PAGE_SIZE } from '../../util/constants';
import { Post } from './objects';
import { Posts } from './mocks';
import { PothosT } from '../../util/pothos';
export const getPost = (t: PothosT) => t.field({
type: Post,
nullable: true,
args: {
id: t.arg.id({ required: true }),
},
resolve: (root, args) => Posts
.get(String(args.id)),
})
export const getPosts = (t: PothosT) => t.field({
type: [Post],
nullable: true,
args: {
take: t.arg.int(),
skip: t.arg.int(),
},
resolve: (root, { skip, take }) => [...Posts.values()]
.slice(
skip ?? 0,
(skip ?? 0) + (take ?? DEFAULT_PAGE_SIZE)
),
}) in resources/posts/requests: import { params, query, types, alias } from 'typed-graphqlify'
import { IPost } from './objects'
export const GET_POSTS_VARS = { take: 5 }
export const GET_POSTS_FIELDS: Partial<IPost>[] = [{
id: types.string,
title: types.string,
}]
// typed-graphqlify input object
export const GET_POSTS = {
posts: params(GET_POSTS_VARS, GET_POSTS_FIELDS),
}
// a typescript type for return value
export const GET_POSTS_TYPE = typeof GET_POSTS;
// a graphql query object
export const GET_POSTS_QUERY = query(GET_POSTS) My remaining issues are:
Currently working on something like: export type GetPostsValue = ReturnType<typeof getPosts> But that only gives me a |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Hey, Not sure if I fully understand everything you are asking for here, so I created an interactive version of your example here: https://stackblitz.com/edit/typescript-nlswbq?file=resources/posts/objects.ts Stack blitz doesn't load tsconfig correctly, so you will need to turn strict mode off and back on again before types are correct in the tsconfig when using the link above. I added some logic to extract types from args defined in pothos: You can define args separately like this: export const getPostArgs = builder.args((t) => ({
id: t.id({ required: true }),
}));
export const getPost = (t: PothosT) =>
t.field({
type: Post,
nullable: true,
args: getPostArgs,
resolve: (root, args) => Posts.get(String(args.id)),
});
export const getPostsArgs = builder.args((t) => ({
take: t.int({ required: false }),
skip: t.int({ required: false }),
}));
export const getPosts = (t: PothosT) =>
t.field({
type: [Post],
nullable: true,
args: getPostsArgs,
resolve: (root, { skip, take }) =>
[...Posts.values()].slice(
skip ?? 0,
(skip ?? 0) + (take ?? DEFAULT_PAGE_SIZE)
),
}); Then you can extract types for the args like this import { InputShapeFromFields } from '@pothos/core';
export const GET_POSTS_VARS: InputShapeFromFields<typeof getPostsArgs> = {
take: 5,
}; Regarding export type GetPostsValue = ReturnType<typeof getPosts> extends FieldRef<
infer T,
any
>
? T
: never; This will get you the expected return type for the resolver of the given field. FieldRefs do not track the arguments that the field accepts, and do not know what fields are available on the type they return. The type that a resolver returns is NOT THE SAME as the types that can be queried for on the client. There is no way in pothos to get a type that represents all the fields available on a type. This limitation exists for a number of reasons, but one of the biggest is that building schemas should be modular, and additional fields for types can be added later either by plugins, or using some of the builder methods like Regarding client types and integration with other tools. Pothos is Code first, but that doesn't mean you can't have a schema file. Every app I've build with pothos generates a schema file that can be used to help with client side type safety. See https://pothos-graphql.dev/docs/guide/printing-schemas and https://pothos-graphql.dev/docs/guide/generating-client-types for examples on how this might be set up. Those docs could probably have some more options and details, but should get you started. Happy to provide more detailed guidance if you have a specific client side tool you are trying to work with. |
Beta Was this translation helpful? Give feedback.
Hey,
Not sure if I fully understand everything you are asking for here, so I created an interactive version of your example here: https://stackblitz.com/edit/typescript-nlswbq?file=resources/posts/objects.ts
Stack blitz doesn't load tsconfig correctly, so you will need to turn strict mode off and back on again before types are correct in the tsconfig when using the link above.
I added some logic to extract types from args defined in pothos:
You can define args separately like this: