GraphQl结合两个解析器

发布于 2025-02-12 07:15:23 字数 2126 浏览 2 评论 0原文

目前,我有两个解析器,作者和书籍,它们从两个单独的API返回数据。在大多数情况下,我只需要致电一个或另一个,但是,在这种情况下,我需要将书籍附加到作者。 我不清楚我该怎么做。

选项1-易于执行

我称其为作者解析器中的API并在此处组合。这意味着我有可能对API进行不必要的电话。这也意味着,如果本书API更改,我必须对作者和书籍解析器进行更新,而不仅仅是更新书籍解析器。

选项2 -Resolver

是否有办法从作者解析器中调用书籍解析器?

选项3-客户端

是否有办法将作者缝合并从客户端查询中进行预订?

我是GraphQL和Type-graphQL的新手,如果这很明显,则很抱歉。

作者

const author = {
  name: 'James',
  bookIds: [1, 2]
};

book

const book = {
  id: 1,
  title: 'Book 1'
};

所需的结果

const author = {
  name: 'James',
  books: [{
    id: 1,
    title: 'Book 1'
  },
  {
    id: 2,
    title: 'Book 2'
  }]
}

解析器

@Service()
@Resolver(() => Author)
export class AuthorResolver {
  constructor(private readonly authorService: authorService) { }

  @Query(() => Author)
  async author(
    @Arg('authorId', () => ID, { nullable: false }) authorId: string,
    @Ctx() { dataSources }: ResolverContext
  ): Promise<Author | undefined> {
    const { authorService } = dataSources;
    const author = await this.author.getAuthor(authorService, authorId);

    return {
      id: author.id,
      name: author.name,
      bookIds: author.bookIds
    };
  }
}

@Service()
@Resolver(() => Book)
export class BookResolver {
  constructor(private readonly bookService: bookService) { }

  @Query(() => Book)
  async book(
    @Arg('bookId', () => ID, { nullable: false }) bookId: string,
    @Ctx() { dataSources }: ResolverContext
  ): Promise<Book | undefined> {
    const { bookService } = dataSources;
    const book = await this.book.getBook(bookService, bookId);

    return {
      id: book.id,
      title: book.title
    };
  }
}

客户端查询

query BookQuery($bookId: ID!) {
  book(bookId: $bookId) {
    id
    title
  }
}

query authorQuery($authorId: ID!) {
  book(authorId: $authorId) {
    id
    name
    books
  }
}

I currently have two resolvers, Authors and Books, that return data from two separate API's. In most scenarios I only need to call one or the other, however, in this scenario, I need to attach the books to the author.
It's not clear to me how I should do this.

Option 1 - Simple to do

I call the book API in the Author resolver and combine them here. This means I'd potentially make unnecessary calls to the Book API. It also means if the book API changes, I'd have to make updates to both the author and book resolvers instead of just updating the Book resolver.

Option 2 - Resolver

Is there a way to call the Book resolver from within the Author resolver?

Options 3 - Client

Is there a way to stitch the author and book together from within the client query?

I’m new to graphql and type-graphql so apologies if this is obvious.

Author

const author = {
  name: 'James',
  bookIds: [1, 2]
};

Book

const book = {
  id: 1,
  title: 'Book 1'
};

Desired outcome

const author = {
  name: 'James',
  books: [{
    id: 1,
    title: 'Book 1'
  },
  {
    id: 2,
    title: 'Book 2'
  }]
}

Resolvers

@Service()
@Resolver(() => Author)
export class AuthorResolver {
  constructor(private readonly authorService: authorService) { }

  @Query(() => Author)
  async author(
    @Arg('authorId', () => ID, { nullable: false }) authorId: string,
    @Ctx() { dataSources }: ResolverContext
  ): Promise<Author | undefined> {
    const { authorService } = dataSources;
    const author = await this.author.getAuthor(authorService, authorId);

    return {
      id: author.id,
      name: author.name,
      bookIds: author.bookIds
    };
  }
}

@Service()
@Resolver(() => Book)
export class BookResolver {
  constructor(private readonly bookService: bookService) { }

  @Query(() => Book)
  async book(
    @Arg('bookId', () => ID, { nullable: false }) bookId: string,
    @Ctx() { dataSources }: ResolverContext
  ): Promise<Book | undefined> {
    const { bookService } = dataSources;
    const book = await this.book.getBook(bookService, bookId);

    return {
      id: book.id,
      title: book.title
    };
  }
}

Client Side Query

query BookQuery($bookId: ID!) {
  book(bookId: $bookId) {
    id
    title
  }
}

query authorQuery($authorId: ID!) {
  book(authorId: $authorId) {
    id
    name
    books
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

嗫嚅 2025-02-19 07:15:23

您必须实现 fieldResolver ,称为books> books,,在作者解析器中。

如果未来的API发生变化,则不会影响解析器,因为您使用与API交谈并充当中间件层的服务。

该服务必须得到很好的实现(抽象),并且返回的实体必须正确匹配/映射到GraphQl对象类型。 IE无需返回{id:rutian.id,...}在解析器内部,因为它是由服务和类映射自动完成的。&nbsp;

此外,您可以在解析器内注入服务实例,因此无需使用@ctx并获取相同的服务实例:只需使用this。[service_name]。[方法] 。

使您的上下文尽可能简单(例如,通过a jwt )。

最终作者解析器更加干净,更便携:

@Resolver(() => Author)
@Service()
export class AuthorResolver {

  @Inject()
  private readonly authorService!: AuthorService;

  @Inject()
  private readonly bookService!: BookService;

  // 'nullable: false' is default behaviour
  // No need for '@Ctx' here
  // Returned value is inferred from service -> 'Promise<Author | undefined>'
  @Query(() => Author)
  async author(@Arg('id', () => ID) id: string) {
    return this.authorService.findOne(id);
  }
  
  @FieldResolver(() => [Book])
  async books(@Root() author: Author) {
    return this.bookService.findManyByAuthorId(author.id);
    // OR 'return this.bookService.findMany(author.bookIds);'
  }
}

如果您想要示例项目,请参见 this

You must implement a FieldResolver, called books, in the Author resolver.

If an API changes in the future, it will not affect the resolver since you use a service that talks to the API and acts as a middleware layer.

The service must be well implemented (abstraction) and the returned entity must be matched/mapped correctly to a GraphQL object type. i.e. there is no need to return {id: author.id, ...} inside a resolver since it's done automatically by the service and class mappings. 

Moreover, you inject a service instance inside the resolver, so there is no need to use @Ctx and obtain the same service instance: simply use this.[SERVICE_NAME].[METHOD].

Keep you context as simple as possible (e.g. authenticated user id obtained by a JWT).

The final Author resolver is much cleaner and more portable:

@Resolver(() => Author)
@Service()
export class AuthorResolver {

  @Inject()
  private readonly authorService!: AuthorService;

  @Inject()
  private readonly bookService!: BookService;

  // 'nullable: false' is default behaviour
  // No need for '@Ctx' here
  // Returned value is inferred from service -> 'Promise<Author | undefined>'
  @Query(() => Author)
  async author(@Arg('id', () => ID) id: string) {
    return this.authorService.findOne(id);
  }
  
  @FieldResolver(() => [Book])
  async books(@Root() author: Author) {
    return this.bookService.findManyByAuthorId(author.id);
    // OR 'return this.bookService.findMany(author.bookIds);'
  }
}

If you want an example project, see this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文