Hector Gonzalez
10/01/2020, 2:14 PMschoolId
. The catch being that the Student schema and School schema are in separate services. Not sure what I'm missing. I am unable to "join" both types using the resolver. Can you kindly help me out? Here´s the code:
*const* VCDB_URI = '<http://localhost:4001/graphql>'
*const* PCDB_URI = '<http://localhost:4002/graphql>'
//Define schema extensions
`*const* typeDefs = ``
extend type Student {
schoolId: School
}
``;`
*const* resolvers = {
Student: {
schoolId(parent, args, context, info) {
return delegateToSchema({
schema: typeDefs,
operation: 'query',
fieldName: 'getBySchoolId',
args: {
schoolId: parent.id,
},
context,
info,
});
}
}
}
//Define the gateway
*const* gateway = new ApolloGateway({
serviceList: [
{ name: 'vcdb', url: VCDB_URI },
{ name: 'pcdb', url: PCDB_URI },
],
extensions: typeDefs,
resolvers: resolvers,
debug: true
});
//Define the server
*const* server = new ApolloServer(
{ gateway,
subscriptions: false,
});
//Start up the server
server.listen({
port: 4000,
*}).then(({ url }) =>* {
`*console.log(*:rocket: *Server ready at ${url}
);*`
});
mbonnin
10/01/2020, 2:19 PMmbonnin
10/01/2020, 2:20 PMHector Gonzalez
10/01/2020, 2:22 PMDariusz Kuc
10/01/2020, 2:42 PMDariusz Kuc
10/01/2020, 2:44 PMDariusz Kuc
10/01/2020, 2:45 PMDariusz Kuc
10/01/2020, 2:46 PMschool
resolvers would extend the student
with additional school
fieldHector Gonzalez
10/01/2020, 2:59 PMschool
and student
entities are already federated, but they are separate services. I'm not sure how they would be able to resolve each others' type without having to be in the same service. Hence I decided to put that logic in the gateway itself, but that approach isn't working either. It's not showing any errors at startup either, it's just not doing the stitching.Dariusz Kuc
10/01/2020, 3:05 PMHector Gonzalez
10/01/2020, 3:15 PMHector Gonzalez
10/01/2020, 3:16 PMDariusz Kuc
10/01/2020, 3:23 PMgraphql-kotlin
then take a look at https://expediagroup.github.io/graphql-kotlin/docs/federated/apollo-federationDariusz Kuc
10/01/2020, 3:25 PMHector Gonzalez
10/01/2020, 3:41 PMDariusz Kuc
10/01/2020, 3:45 PMHector Gonzalez
10/01/2020, 4:17 PMHector Gonzalez
10/01/2020, 4:20 PMgetReviewByProductId(id)
, I assume this method is the resolver for the Review, correct? and it's all part of the same project and service? The problem we are facing is that we are trying to fetch from a different project and service, so I'm not sure how to wire that up, if at all possible.Dariusz Kuc
10/01/2020, 4:31 PMProduct
and how to fetch it
• service B knows about Reviews
and how to fetch it, it “knows” that Product
type exists somewhere so it just adds new field to it to fetch reviews (as it subject matter expert on how to fetch reviews)Hector Gonzalez
10/06/2020, 9:24 PM@ExternalDirective
annotation to schoolName. But when I do that, the gateway errors out with this message: This data graph is missing a valid configuration. [vcdb] School.schoolName -> is marked as @external but is not used by a @requires, @key, or @provides directive.
Hector Gonzalez
10/06/2020, 9:25 PMHector Gonzalez
10/06/2020, 9:29 PM@ExternalDirective
from schoolName in service B, the gateway starts fine and I can retrieve Student -> School -> schoolId
But for some reason Student -> School -> schoolName
won't workDariusz Kuc
10/06/2020, 9:48 PMDariusz Kuc
10/06/2020, 9:50 PMSchool
and extends Student
with additional school
field
• service B defines Student
Dariusz Kuc
10/06/2020, 9:50 PMSchool
it should have all the logic related to fetching that dataHector Gonzalez
10/06/2020, 10:00 PMHector Gonzalez
10/07/2020, 8:57 PMHector Gonzalez
10/07/2020, 8:59 PMHector Gonzalez
10/07/2020, 9:00 PMHector Gonzalez
10/07/2020, 9:01 PMHector Gonzalez
10/13/2020, 7:24 PMDariusz Kuc
10/13/2020, 7:29 PMHector Gonzalez
10/13/2020, 7:44 PM@KeyDirective(fields = FieldSet("studentId"))
@ExtendsDirective
class Student(@ExternalDirective val studentId: Long,
@GraphQLIgnore val schoolId: Long
){
fun school(): School? = School(99,"Hardcoded")
}
Dariusz Kuc
10/13/2020, 8:14 PMHector Gonzalez
10/14/2020, 7:26 PM@Component
class StudentResolver(private val schoolRepository: SchoolRepository) : FederatedTypeResolver<Student> {
override suspend fun resolve(environment: DataFetchingEnvironment, representations: List<Map<String, Any>>): List<Student?> =
representations.map {
val studentId = it["studentId"]?.toString()?.toLongOrNull() ?: throw InvalidStudentIdException()
val schoolId = it["schoolId"]?.toString()?.toLongOrNull() ?: throw InvalidSchoolIdException()
val school = schoolRepository.findSchoolBySchoolId(schoolId)
if(school!=null){
Student(studentId, schoolId, school)
} else {
null
}
}
class InvalidStudentIdException : RuntimeException()
class InvalidSchoolIdException : RuntimeException()
}
And added the resolver to the federated type registry in the SpringBootApplication class:
@SpringBootApplication
class DemoApplication
@Bean
fun federatedTypeRegistry(studentResolver: StudentResolver): FederatedTypeRegistry =
FederatedTypeRegistry(mapOf("Student" to studentResolver))
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
Hector Gonzalez
10/14/2020, 7:29 PMHector Gonzalez
10/14/2020, 7:30 PMHector Gonzalez
10/14/2020, 7:32 PMQueryPlan {
Sequence {
Fetch(service: "vcdb") {
{
getByStudentId(studentId: 1) {
schoolId
studentId
studentName
__typename
}
}
},
Flatten(path: "getByStudentId") {
Fetch(service: "pcdb") {
{
... on Student {
__typename
studentId
}
} =>
{
... on Student {
school {
schoolName
}
}
}
},
},
},
}
Dariusz Kuc
10/14/2020, 7:42 PM@SpringBootApplication
class DemoApplication {
@Bean
fun federatedTypeRegistry(studentResolver: StudentResolver): FederatedTypeRegistry =
FederatedTypeRegistry(mapOf("Student" to studentResolver))
}
i.e. @Bean
definitions should be included inside configuration class and not top level functionsHector Gonzalez
10/14/2020, 7:48 PMDariusz Kuc
10/14/2020, 7:49 PM