I'm facing errors using Rover CLI to compose my subgraphs, however I don't face any issues with the gateway approach using the same code.
I'm using Nestjs graphql driver.
It has been a blocker for a couple of days. any help would be appreciated. thank you.
The errors :
$ rover supergraph compose --config ./supergraph.yaml -o supergraph.graphql
resolving SDL for subgraphs defined in ./supergraph.yaml
composing supergraph with Federation v2.3.2.exe
error[E029]: Encountered 28 build errors while trying to build a supergraph.
Caused by:
UNKNOWN: The following supergraph API query:
mutation {
refreshToken {
user {
resources {
...
}
}
}
}
cannot be satisfied by the subgraphs because:
- from subgraph "users":
- cannot find field "UserDto.resources".
- cannot move to subgraph "resources", which has field "UserDto.resources", because type "UserDto" has no @key defined in subgraph "resources".
UNKNOWN: The following supergraph API query:
mutation {
refreshToken {
user {
rooms {
...
}
}
}
}
cannot be satisfied by the subgraphs because:
- from subgraph "users":
- cannot find field "UserDto.rooms".
- cannot move to subgraph "rooms", which has field "UserDto.rooms", because type "UserDto" has no @key defined in subgraph "rooms".
users dto and it's generated schema :
import { Directive, ID, ObjectType, registerEnumType } from '@nestjs/graphql';
import { FilterableField, IDField } from '@ptc-org/nestjs-query-graphql';
import { IUser, UserRole } from '@core';
@ObjectType()
@Directive('@key(fields: "id")')
export class UserDto implements Omit<IUser, 'password' | 'hashedRefreshToken'> {
@IDField(() => ID)
id: string;
@FilterableField(() => String)
firstName: string;
@FilterableField()
lastName: string;
@FilterableField()
username: string;
@FilterableField()
email: string;
@FilterableField()
verified: boolean;
@FilterableField({
nullable: true,
})
birthday?: Date;
@FilterableField(() => UserRole)
role: UserRole;
@FilterableField()
createdAt: Date;
@FilterableField()
updatedAt: Date;
constructor(user: UserDto) {
Object.assign(this, user);
}
}
registerEnumType(UserRole, {
name: 'UserRole',
});
###################################################################################################
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
type UserDto {
id: ID!
firstName: String!
lastName: String!
username: String!
email: String!
verified: Boolean!
birthday: DateTime
role: UserRole!
createdAt: DateTime!
updatedAt: DateTime!
}
"""
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
"""
scalar DateTime
enum UserRole {
ADMIN
USER
}
"""Login response"""
type LoginResponseDto {
accessToken: String!
refreshToken: String!
user: UserDto!
}
"""Register response"""
type RegisterResponse {
accessToken: String!
refreshToken: String!
user: UserDto!
}
type Query {
me: UserDto!
}
type Mutation {
"""Login a user with username or email and password, returns JWT token."""
login(credentials: Credentials!): LoginResponseDto!
register(userInfo: RegisterUserDto!): RegisterResponse!
refreshToken: LoginResponseDto!
}
"""Login user"""
input Credentials {
email: String
username: String
password: String!
}
"""Create new user"""
input RegisterUserDto {
email: String!
firstName: String!
password: String!
lastName: String!
username: String!
birthday: DateTime
}
rooms dto and it's generated schema :
import {
Directive,
Field,
GraphQLISODateTime,
ID,
ObjectType,
registerEnumType,
} from '@nestjs/graphql';
import { IRoom, IRoomDatabaseEntity, RoomType } from '@core';
import { FilterableField, IDField } from '@ptc-org/nestjs-query-graphql';
import { UserDto } from './user-dto.directive';
@ObjectType()
@Directive('@key(fields: "id")')
export class RoomDto implements Omit<IRoom, keyof IRoomDatabaseEntity> {
@IDField(() => ID)
id: string;
@FilterableField()
title: string;
@FilterableField(() => String, { nullable: true })
description?: string;
@FilterableField(() => RoomType)
roomType: RoomType;
@Field(() => [String])
resourceIds: string[];
@FilterableField(() => GraphQLISODateTime)
createdAt: Date;
@FilterableField(() => GraphQLISODateTime)
updatedAt: Date;
@Field(() => UserDto)
author: UserDto;
@Field(() => [UserDto])
participants: UserDto[];
@Field(() => [UserDto])
owners: UserDto[];
@Field(() => [UserDto])
moderators: UserDto[];
@Field(() => [UserDto])
collaborators: UserDto[];
}
registerEnumType(RoomType, {
name: 'RoomType',
});
####################################################################################
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
type UserDto {
id: ID!
rooms: [RoomDto!]!
moderatedRooms: [RoomDto!]!
participatedRooms: [RoomDto!]!
collaboratedRooms: [RoomDto!]!
}
type RoomDto {
id: ID!
title: String!
description: String
roomType: RoomType!
resourceIds: [String!]!
createdAt: DateTime!
updatedAt: DateTime!
author: UserDto!
participants: [UserDto!]!
owners: [UserDto!]!
moderators: [UserDto!]!
collaborators: [UserDto!]!
}
enum RoomType {
PUBLIC
PRIVATE
}
"""
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
"""
scalar DateTime
type DeleteResponse {
message: String!
success: Boolean!
}
type Query {
room(id: String!): RoomDto!
rooms: [RoomDto!]!
}
type Mutation {
createRoom(room: CreateRoomInput!): RoomDto!
deleteRoom(id: String!): DeleteResponse!
}
"""Input to create a new room"""
input CreateRoomInput {
description: String
title: String!
roomType: String
resourceIds: [String!]!
}
resources dto and it's generated schema :
import {
Directive,
Field,
GraphQLISODateTime,
ID,
ObjectType,
registerEnumType,
} from '@nestjs/graphql';
import { FilterableField, IDField } from '@ptc-org/nestjs-query-graphql';
import { IResource, ResourceFormat, ResourceType } from '@core';
import { UserDto } from './user-refrence.dto';
@ObjectType('Resource', {
description: 'Resources uploaded by users',
})
@Directive('@key(fields: "id")')
export class ResourceDto implements IResource {
@IDField(() => ID)
id: string;
@FilterableField()
title: string;
@FilterableField()
description?: string;
@FilterableField(() => ResourceType)
type: ResourceType;
@FilterableField(() => ResourceFormat)
format: ResourceFormat;
@FilterableField(() => GraphQLISODateTime)
createdAt: Date;
@FilterableField(() => GraphQLISODateTime)
updatedAt: Date;
@Field(() => UserDto)
author: UserDto;
@Field(() => String, {
nullable: true,
})
indexed: string;
@Field(() => String, {
nullable: true,
})
raw: string;
}
registerEnumType(ResourceType, {
name: 'ResourceType',
});
registerEnumType(ResourceFormat, {
name: 'ResourceFormat',
});
####################################################################################
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
type UserDto {
id: ID!
resources: [Resource!]!
}
"""Resources uploaded by users"""
type Resource {
id: ID!
title: String!
description: String!
type: ResourceType!
format: ResourceFormat!
createdAt: DateTime!
updatedAt: DateTime!
author: UserDto!
indexed: String
raw: String
}
enum ResourceType {
BOOK
VIDEO
AUDIO
IMAGE
DOCUMENT
ARTICLE
CODE
OTHER
}
enum ResourceFormat {
PDF
DOC
DOCX
XLS
XLSX
PPT
PPTX
MP3
MP4
WAV
AVI
MKV
JPG
JPEG
PNG
GIF
OTHER
}
"""
A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
"""
scalar DateTime
type DeleteResponse {
message: String!
success: Boolean!
}
type Query {
getOneResource(id: String!): Resource!
getAllResources: [Resource!]!
}
type Mutation {
createResource(resource: CreateResourceInput!): Resource!
deleteResource(id: String!): DeleteResponse!
}
"""Create new resource"""
input CreateResourceInput {
description: String!
format: String!
title: String!
type: String!
}
user/resources reference
import { Directive, Field, ID, ObjectType } from '@nestjs/graphql';
import { ResourceDto } from './resource.dto';
@ObjectType()
@Directive('@key(fields: "id")')
export class UserDto {
@Field(() => ID)
id!: string;
@Field(() => [ResourceDto])
resources!: ResourceDto[];
}
user/rooms reference
import { Directive, Field, ID, ObjectType } from '@nestjs/graphql';
import { RoomDto } from './room.dto';
@ObjectType()
@Directive('@key(fields: "id")')
export class UserDto {
@Field(() => ID)
id!: string;
@Field(() => [RoomDto])
rooms!: RoomDto[];
}
It's expected that Rover successfully compose the subgraphs into supergraph.