Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finding resolver for Entity: type not found #2923

Open
niklod opened this issue Feb 12, 2024 · 2 comments
Open

Finding resolver for Entity: type not found #2923

niklod opened this issue Feb 12, 2024 · 2 comments

Comments

@niklod
Copy link

niklod commented Feb 12, 2024

What happened?

I’m trying to setup communication via GQL federation between two services.
Service A serves reviews and service B resolves review's author by ID.

Getting an error when trying to resolve nil extended entity Author

finding resolver for Entity "Author": type not found for Author

For some reason, federation (or gqlgen?) trying to resolve a nil value Author

What did you expect?

When I return nil Author (in service A), resolver FindAuthorBySlug (in service B) ignores it and doesn't try to resolve the nil value.

Minimal graphql.schema and models to reproduce

GraphQL schema (Service A)

extend type Query {
    reviews: ReviewsQuery
}

type ReviewsQuery {
    reviews: [Review!]
}

type Review {
    id: ID!
    author: Author @provides(fields: "slug")
}

extend type Author @key(fields: "slug"){
    slug: String! @external
}

GraphQL schema(Service B)

type Author @key(fields: "slug"){
    slug: String!
    name: String
}

FindAuthorBySlug resolver (generated by gqlgen in Service B)

func (r *entityResolver) FindAuthorBySlug(ctx context.Context, slug string) (*model.Author, error) {
	switch slug {
	case "author-a":
		return &model.Author{
			Slug: "author-a",
			Name: pointer.To("Author A"),
		}, nil
	case "author-b":
		return &model.Author{
			Slug: "author-b",
			Name: pointer.To("Author B"),
		}, nil
	default:
		return nil, nil
	}
}

Reviews resolver (generated by gqlgen in Service A)

func (r *reviewsQueryResolver) Reviews(ctx context.Context, obj *models.ReviewsQuery) ([]models.Review, error) {
	return []models.Review{
		{
			ID: "review-a",
			Author: &models.Author{
				Slug: "author-a",
			},
		},
		{
			ID:     "review-c",
			Author: nil,
		},
		{
			ID: "review-b",
			Author: &models.Author{
				Slug: "author-b",
			},
		},
	}, nil
}

Code, where error happened

func entityResolverNameForDiablo4Classes(ctx context.Context, rep map[string]interface{}) (string, error) {
	for {
		var (
			m   map[string]interface{}
			val interface{}
			ok  bool
		)
		_ = val
		m = rep
		if _, ok = m["slug"]; !ok {
			break
		}
		return "findDiablo4ClassesBySlug", nil
	}
----> return "", fmt.Errorf("%w for Diablo4Classes", ErrTypeNotFound)
}

versions

github.com/99designs/gqlgen v0.17.41
github.com/wundergraph/graphql-go-tools v1.52.1
@farawaysouthwest
Copy link

Hi there, Just passing through, I'm not a maintainer of gqlgen, but I know a thing or two about Federation.

You need to invert your type extension. If I understand correctly, the end goal is to add Author to Review?

Adding a field to a type always needs to be done by the service that knows about the data that is to be added.

So, in this case, Service B is presumably the one that provides all the Author data, it needs to be the Service that adds the field to Review, not Service A, which knows nothing about authors.

In your gqlgen entity resolver, you should be able to pass the autherId or slug or whatever your using to resolve the author field.

Service A

extend type Query {
    reviews: ReviewsQuery
}

type ReviewsQuery {
    reviews: [Review!]
}

type Review @key(fields: "id") {
    id: ID!
}

Service B

type Author {
    slug: String!
    name: String
}

type Review @key(fields: "id") {
    id: ID!
    author: Author
}

@niklod
Copy link
Author

niklod commented Feb 15, 2024

@farawaysouthwest Hi! Thanks for your answer. Yep, this approach should work.
But resolving entity directly by ID (Not by attaching field to the parent entity "Review") also should be available in such federations.
I already had experience with such approach but now it doesn't work for some reason

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants