GraphQL - 定义类型
GraphQL - defining types
我正在努力学习 Ben Awad 的 lireddit 教程。
在他做教程的时候,可能对Field类型有不同的推断。
我正在尝试将字段添加到我的关系属性(将创建者字段添加到 Post),这样我就可以访问该 post 记录上的创建者属性。
Ben 的操作如下:
@Field()
@ManyToOne(() => User, (user) => user.posts)
creator: User;
这对他有用。当我尝试这个时,我收到一条错误消息:
throw new errors_1.NoExplicitTypeError(prototype.constructor.name, propertyKey,
parameterIndex, argName);
NoExplicitTypeError: Unable to infer GraphQL type from TypeScript
reflection system. You need to provide explicit type for 'creator' of
'Post' class.
当我查看 GraphQL docs 以了解如何为创建者提供显式类型时,我找不到类似的示例(对我来说足够简单以破译我可以应用的原则)。
我对文档感到困惑,因为有以下示例:
任何人都可以看到我需要做什么才能将字段识别为我可以读取的对象吗?
@ObjectType()
class Rate {
@Field(type => Int)
value: number;
@Field()
date: Date;
user: User;
}
我认为他们使用 user: User 的方式与我使用 creator: User 的方式相同。 Field() 不能与 ObjectType() 具有相同的东西是有原因的吗?
我试过了:
@Field(() => [User])
@ManyToOne(() => User, (user) => user.posts)
creator: User;
这不会给出任何错误(代码也不会像 Ben 那样),直到我到达操场,在这种情况下,我无法 return 用户数据 - 非常清楚这是错的。也不清楚数组是指用户对象的属性数组,还是用户数组(这也是错误的)。我可以从 GraphQL 文档中看到,应该可以将字段属性定义为对象类型,但我找不到说明如何执行此操作的示例。
我看过 ,这看起来是一个类似的问题,但我无法从建议的答案中看出如何将这些想法应用到这个问题中。
我看过 this post,它有类似的问题,并通过参考示例来回答,该示例说明如何编写查找关系的解析器,但我的解析器已经找到了 creatorId,所以我想也许我没有在正确的地方寻找答案。
在我的 post 解析器中,我有:
import {
Resolver,
Query,
Arg,
Mutation,
InputType,
Field,
Ctx,
UseMiddleware,
Int,
FieldResolver,
Root,
ObjectType,
} from "type-graphql";
import { Post } from "../entities/Post";
import { MyContext } from "../types";
import { isAuth } from "../middleware/isAuth";
import { getConnection } from "typeorm";
@InputType()
class PostInput {
@Field()
title: string;
@Field()
text: string;
}
@ObjectType()
class PaginatedPosts {
@Field(() => [Post])
posts: Post[];
@Field()
hasMore: boolean;
}
@Resolver(Post)
export class PostResolver {
@FieldResolver(() => String)
textSnippet(@Root() post: Post) {
return post.text.slice(0, 50);
}
@Query(() => PaginatedPosts)
async posts(
@Arg("limit", () => Int) limit: number,
@Arg("cursor", () => String, { nullable: true }) cursor: string | null
): Promise<PaginatedPosts> {
// 20 -> 21
const realLimit = Math.min(50, limit);
const reaLimitPlusOne = realLimit + 1;
const qb = getConnection()
.getRepository(Post)
.createQueryBuilder("p")
.orderBy('"createdAt"', "DESC")
.take(reaLimitPlusOne);
if (cursor) {
qb.where('"createdAt" < :cursor', {
cursor: new Date(parseInt(cursor)),
});
}
const posts = await qb.getMany();
return {
posts: posts.slice(0, realLimit),
hasMore: posts.length === reaLimitPlusOne,
};
}
@Query(() => Post, { nullable: true })
post(@Arg("id") id: number): Promise<Post | undefined> {
return Post.findOne(id);
}
@Mutation(() => Post)
@UseMiddleware(isAuth)
async createPost(
@Arg("input") input: PostInput,
@Ctx() { req }: MyContext
): Promise<Post> {
return Post.create({
...input,
creatorId: req.session.userId,
}).save();
}
@Mutation(() => Post, { nullable: true })
async updatePost(
@Arg("id") id: number,
@Arg("title", () => String, { nullable: true }) title: string
): Promise<Post | null> {
const post = await Post.findOne(id);
if (!post) {
return null;
}
if (typeof title !== "undefined") {
await Post.update({ id }, { title });
}
return post;
}
@Mutation(() => Boolean)
async deletePost(@Arg("id") id: number): Promise<boolean> {
await Post.delete(id);
return true;
}
}
首先,creator
应该是User
类型,而不是用户列表,即
@Field(() => User)
@ManyToOne(() => User, (user) => user.posts)
creator: User;
当您检索 post 时,您应该在查询中包含一个关系,因此用户实体也会被加载:
@Query(() => Post, { nullable: true })
post(@Arg("id") id: number): Promise<Post | undefined> {
return Post.findOne(id, { relations: "creator" });
}
此外,当您使用查询构建器获取 post 时,您应该添加用户实体:
const qb = getConnection()
.getRepository(Post)
.createQueryBuilder("p")
.leftJoinAndSelect("p.creator", "p_creator")
.orderBy('"createdAt"', "DESC")
.take(reaLimitPlusOne);
奖金说明:
GraphQL 中存在过度获取数据的常见问题,因此查询会随着时间的推移而变慢。
以这种方式,您还可以考虑将 creator 字段移动到 FieldResolver,以便仅在请求时才从数据库中检索它。如果你这样做,另一个关于 ManyToOne 关系的好做法是使用数据加载器,所以如果你,例如,从同一个创建者加载 10 posts,你最终只会有一个获取操作该创建者而不是对数据库的 10 个请求。 Ben Awad 也提供了很棒的教程和解释:https://www.youtube.com/watch?v=uCbFMZYQbxE.
这对于本教程来说不是必需的,但如果您要构建一些重要的应用程序,则必须知道。
我正在努力学习 Ben Awad 的 lireddit 教程。
在他做教程的时候,可能对Field类型有不同的推断。
我正在尝试将字段添加到我的关系属性(将创建者字段添加到 Post),这样我就可以访问该 post 记录上的创建者属性。
Ben 的操作如下:
@Field()
@ManyToOne(() => User, (user) => user.posts)
creator: User;
这对他有用。当我尝试这个时,我收到一条错误消息:
throw new errors_1.NoExplicitTypeError(prototype.constructor.name, propertyKey,
parameterIndex, argName);
NoExplicitTypeError: Unable to infer GraphQL type from TypeScript reflection system. You need to provide explicit type for 'creator' of 'Post' class.
当我查看 GraphQL docs 以了解如何为创建者提供显式类型时,我找不到类似的示例(对我来说足够简单以破译我可以应用的原则)。
我对文档感到困惑,因为有以下示例:
任何人都可以看到我需要做什么才能将字段识别为我可以读取的对象吗?
@ObjectType()
class Rate {
@Field(type => Int)
value: number;
@Field()
date: Date;
user: User;
}
我认为他们使用 user: User 的方式与我使用 creator: User 的方式相同。 Field() 不能与 ObjectType() 具有相同的东西是有原因的吗?
我试过了:
@Field(() => [User])
@ManyToOne(() => User, (user) => user.posts)
creator: User;
这不会给出任何错误(代码也不会像 Ben 那样),直到我到达操场,在这种情况下,我无法 return 用户数据 - 非常清楚这是错的。也不清楚数组是指用户对象的属性数组,还是用户数组(这也是错误的)。我可以从 GraphQL 文档中看到,应该可以将字段属性定义为对象类型,但我找不到说明如何执行此操作的示例。
我看过
我看过 this post,它有类似的问题,并通过参考示例来回答,该示例说明如何编写查找关系的解析器,但我的解析器已经找到了 creatorId,所以我想也许我没有在正确的地方寻找答案。
在我的 post 解析器中,我有:
import {
Resolver,
Query,
Arg,
Mutation,
InputType,
Field,
Ctx,
UseMiddleware,
Int,
FieldResolver,
Root,
ObjectType,
} from "type-graphql";
import { Post } from "../entities/Post";
import { MyContext } from "../types";
import { isAuth } from "../middleware/isAuth";
import { getConnection } from "typeorm";
@InputType()
class PostInput {
@Field()
title: string;
@Field()
text: string;
}
@ObjectType()
class PaginatedPosts {
@Field(() => [Post])
posts: Post[];
@Field()
hasMore: boolean;
}
@Resolver(Post)
export class PostResolver {
@FieldResolver(() => String)
textSnippet(@Root() post: Post) {
return post.text.slice(0, 50);
}
@Query(() => PaginatedPosts)
async posts(
@Arg("limit", () => Int) limit: number,
@Arg("cursor", () => String, { nullable: true }) cursor: string | null
): Promise<PaginatedPosts> {
// 20 -> 21
const realLimit = Math.min(50, limit);
const reaLimitPlusOne = realLimit + 1;
const qb = getConnection()
.getRepository(Post)
.createQueryBuilder("p")
.orderBy('"createdAt"', "DESC")
.take(reaLimitPlusOne);
if (cursor) {
qb.where('"createdAt" < :cursor', {
cursor: new Date(parseInt(cursor)),
});
}
const posts = await qb.getMany();
return {
posts: posts.slice(0, realLimit),
hasMore: posts.length === reaLimitPlusOne,
};
}
@Query(() => Post, { nullable: true })
post(@Arg("id") id: number): Promise<Post | undefined> {
return Post.findOne(id);
}
@Mutation(() => Post)
@UseMiddleware(isAuth)
async createPost(
@Arg("input") input: PostInput,
@Ctx() { req }: MyContext
): Promise<Post> {
return Post.create({
...input,
creatorId: req.session.userId,
}).save();
}
@Mutation(() => Post, { nullable: true })
async updatePost(
@Arg("id") id: number,
@Arg("title", () => String, { nullable: true }) title: string
): Promise<Post | null> {
const post = await Post.findOne(id);
if (!post) {
return null;
}
if (typeof title !== "undefined") {
await Post.update({ id }, { title });
}
return post;
}
@Mutation(() => Boolean)
async deletePost(@Arg("id") id: number): Promise<boolean> {
await Post.delete(id);
return true;
}
}
首先,creator
应该是User
类型,而不是用户列表,即
@Field(() => User)
@ManyToOne(() => User, (user) => user.posts)
creator: User;
当您检索 post 时,您应该在查询中包含一个关系,因此用户实体也会被加载:
@Query(() => Post, { nullable: true })
post(@Arg("id") id: number): Promise<Post | undefined> {
return Post.findOne(id, { relations: "creator" });
}
此外,当您使用查询构建器获取 post 时,您应该添加用户实体:
const qb = getConnection()
.getRepository(Post)
.createQueryBuilder("p")
.leftJoinAndSelect("p.creator", "p_creator")
.orderBy('"createdAt"', "DESC")
.take(reaLimitPlusOne);
奖金说明: GraphQL 中存在过度获取数据的常见问题,因此查询会随着时间的推移而变慢。 以这种方式,您还可以考虑将 creator 字段移动到 FieldResolver,以便仅在请求时才从数据库中检索它。如果你这样做,另一个关于 ManyToOne 关系的好做法是使用数据加载器,所以如果你,例如,从同一个创建者加载 10 posts,你最终只会有一个获取操作该创建者而不是对数据库的 10 个请求。 Ben Awad 也提供了很棒的教程和解释:https://www.youtube.com/watch?v=uCbFMZYQbxE.
这对于本教程来说不是必需的,但如果您要构建一些重要的应用程序,则必须知道。