1

I'm using NestJS + Prisma + Apollo Federation.

On microservice A is the definition of user, on microservice B is defined posts. The relation is 1 - N, a user can have N posts.

In Prisma, datamodel of Post is defined with a String for user, since userId is a uuid.

type Post {
  id: Int! @id
  createdAt: DateTime! @createdAt
  updatedAt: DateTime! @updatedAt
  user: String!
}

In generated schema (with https://graphql-code-generator.com), Post has a attribute of type User, and this type User extends the id and a array of posts:

type Post @key(fields: "id") {
  id: Int!
  createdAt: DateTime!
  updatedAt: DateTime!
  user: User!
}

extend type User @key(fields: "id") {
  id: ID! @external
  posts: [Post]
}

In apollo federation, all works as expected, except when a query is made trying to link between both microservices.

On playground, if you try to query posts with its user without setting subfields, it breaks the schema and say you have to set the subfields of User, and if you set the subfields graphql responds with a message that you cannot use subfields because its type is String.

The only way that I could make this work correctly was setting in Prisma a userId field of type string and setting another field in schema called user of type User. But all the examples didn't show a field to work with db and a field to work with schema.

My question is if that is the recommended or am I missing something.

arceliver
  • 336
  • 1
  • 11

1 Answers1

0

In order to get User from Post, you have to create a resolver in your post and user service.

Post Service

const resolvers = {
  Post:{//before you do this you have to extend User schema which you already did.
      // you are basically asking the 'User' service, which field should be used to query user.
    user: ref => ({ __typename: 'User', id: ref.userId })
  }
  Query:{
    // query resolvers
  },
  Mutation:{
    // mutation resolvers
  }

User service

const resolvers = {
  User:{//the code below allows other services to extend User in their own schemas
    __resolveReference: (ref, { userDataLoader }) => userDataLoader.load(ref.id),
  }
  Query:{
    // query resolvers
  },
  Mutation:{
    // mutation resolvers
  }

Now linking arrays like [Post] must be done purely in the post service

Post Service

const resolvers = {
  Post:{//before you do this you have to extend User schema which you already did.
      // you are basically telling the user service, which field should be used to query user.
    user: ref => ({ __typename: 'User', id: ref.user })
  },
  User:{
    posts:(ref, args, {postDataLoader}) => getOrders(ref.postIds) //or ref.userId(foreign key)
  },
  Query:{
    // query resolvers
  },
  Mutation:{
    // mutation resolvers
  }
Sihoon Kim
  • 1,481
  • 2
  • 13
  • 32
  • Just to make sure, you don't need to create relations in the DB schema, right? If I understood correctly, at DB level no relations are needed, since Apollo federation will resolve them in the resolvers, is that right? – Otavio Bonder Feb 09 '21 at 18:31
  • In general, each service will have its own database and set of tables. The data in a single database should have relations in DB level. But for the cases where data is stored in another service(separate database), the relation is made in the server. And also its not possible to make relations in DB level in this case because the two sets of data are in two different databases – Sihoon Kim Feb 10 '21 at 09:29
  • Yes, perfect Sihoon. That's what I was thinking. Thank you – Otavio Bonder Feb 10 '21 at 12:29