【问题标题】:GraphQL - defining typesGraphQL - 定义类型
【发布时间】:2021-11-20 00:54:20
【问题描述】:

我正在尝试学习 Ben Awad 的 lireddit 教程。

在他制作教程的时候,可能对字段类型有不同的推论。

我正在尝试将字段添加到我的关系属性(将创建者字段添加到帖子),以便我可以访问该帖子记录上的创建者属性。

Ben 这样做如下:

@Field()
  @ManyToOne(() => User, (user) => user.posts)
  creator: User;

这对他有用。当我尝试这个时,我收到一条错误消息:

 throw new errors_1.NoExplicitTypeError(prototype.constructor.name, propertyKey,

参数索引,参数名称);

NoExplicitTypeError:无法从 TypeScript 推断 GraphQL 类型 反射系统。您需要为“创建者”提供显式类型 “发布”类。

当我查看 GraphQL docs 以了解如何为创建者提供显式类型时,我找不到类似的示例(简单到足以让我破译我可以应用的原则)。

我对文档感到困惑,因为有以下示例:

谁能看到我需要做什么才能要求将该字段识别为我可以读取的对象?

@ObjectType()
class Rate {
  @Field(type => Int)
  value: number;

  @Field()
  date: Date;

  user: User;
}

我认为他们使用用户:用户的方式与我使用创建者:用户的方式相同。 Field() 不能和 ObjectType() 有相同的东西是有原因的吗?

我试过了:

@Field(() => [User])
  @ManyToOne(() => User, (user) => user.posts)
  creator: User;

这不会产生任何错误(代码也不会像 Ben 那样),直到我到达操场,在这种情况下,我无法返回用户数据 - 很明显这是错误的。也不清楚该数组是指用户对象上的属性数组还是用户数组(这也是错误的)。我可以从 GraphQL 文档中看到,应该可以将字段属性定义为对象类型,但我找不到说明如何做到这一点的示例。

我见过this post,这看起来像一个类似的问题,但我无法从建议的答案中看到如何将这些想法应用于这个问题。

我见过this post,它有类似的问题,并通过参考一个示例来回答,该示例显示如何编写查找关系的解析器,但我的解析器已经找到了 creatorId,所以我想也许我我没有在正确的地方寻找答案。

在我的帖子解析器中,我有:

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;
  }
}

【问题讨论】:

    标签: graphql typegraphql


    【解决方案1】:

    首先,creator 应该是 User 类型,而不是用户列表,即

    @Field(() => User)
    @ManyToOne(() => User, (user) => user.posts)
    creator: User;
    

    当您检索帖子时,您应该在查询中包含一个关系,这样 User 实体也会被加载:

    @Query(() => Post, { nullable: true })
    post(@Arg("id") id: number): Promise<Post | undefined> {
      return Post.findOne(id, { relations: "creator" });
    }
    

    此外,当您使用查询构建器获取帖子时,您应该添加用户实体:

    const qb = getConnection()
      .getRepository(Post)
      .createQueryBuilder("p")
      .leftJoinAndSelect("p.creator", "p_creator")
      .orderBy('"createdAt"', "DESC")
      .take(reaLimitPlusOne);
    

    注意事项: GraphQL 中存在过度获取数据的常见问题,因此查询会随着时间的推移而变慢。 以这种方式,您还可以考虑将 creator 字段移动到 FieldResolver,以便仅在请求时才从数据库中检索它。如果你这样做了,ManyToOne 关系的另一个好习惯是使用数据加载器,因此,例如,如果你从同一个创建者那里加载 10 个帖子,你最终会得到该创建者的一个获取操作,而不是 10 个对数据库的请求。 Ben Awad 也提供了一个很棒的教程和解释:https://www.youtube.com/watch?v=uCbFMZYQbxE

    这对于本教程来说不是特别必要的,但如果您正在构建一些严肃的应用程序,这是必须知道的。

    【讨论】:

    • 谢谢!当我尝试这个时,我得到:没有重载匹配这个调用。重载 1 of 3, '(this: ObjectType, id?: string | number | Date | ObjectID | undefined, options?: FindOneOptions | undefined): Promise<...>',给出以下错误.类型 'string' 不可分配给类型 'string[] |不明确的'。 Overload 2 of 3, '(this: ObjectType, conditions?: FindConditions | undefined, options?: FindOneOptions | undefined): Promise<...>',给出了以下错误。 'number' 类型的参数不可分配给 'FindConditions 类型的参数 |未定义'。
    • 另外,当我尝试在 graphql 游乐场中使用它时,我收到一条错误消息:“列 \"createdAt\" 不存在”。如果我注释查询的 orderBy 行,该错误就会消失。
    • 你知道我可以在哪里调查由 leftJoinandSelect 行引入的问题的可能来源吗?我不明白为什么 p.creator 在该行中包含两次。我不知道在哪里寻找资源以更好地了解这条线在做什么。也许那条线有一个线索。
    • 注意:我仍然有上面提到的问题,但是如果我将最后一行中的 'take' 替换为 'limit' 则查询有效。感谢您的帮助。
    • createdAt 错误是由于多余的引号引起的。它应该是: .orderBy("createdAt", "DESC") 左连接和选择基本上意味着包含创建者表并返回其字段,其中“p_creator”只是此查询中未来使用的别名。它没有被使用,但它是强制性的。是的,如果您在查询中加入,您应该使用 limit 而不是 take,抱歉我错过了 :)
    猜你喜欢
    • 2016-10-21
    • 2020-03-01
    • 2021-03-04
    • 2020-06-01
    • 2020-10-04
    • 1970-01-01
    • 2018-10-19
    • 2017-09-18
    • 2021-04-01
    相关资源
    最近更新 更多