1
votes

I have the following structure which I would consider fairly common. I've been looking and can't find any information on the recommended way for schema stitched services to call each other (while still being used by the front end). Here is my situation:

Api Gateway schema stitches:

  • Users Service
  • Posts Service

The posts service uses mongodb and the users service uses PostgreSQL. The posts service's schema only includes the user_id and not the full user. When I need a page of posts (with the corresponding user information) I am calling the users service using node-fetch from within the getPosts resolver in the posts service like so:

{
  getUsers(user_id__in:[1,5,9,3]){
    username
    join_date
  }
}

This solution feels kind of "dirty" compared to how elegant the rest of the apollo-graphql has been.

Is it more typically to ignore the graphql portions for resolving data and use the typical rest endpoint structure provided by apollo-graphql's underlying express server? If not should I be calling the other services directly or calling them through the api gateway? If I should be calling them through the api gateway is there a way any built in elegant way of calling the api gateway using some Apollo functionality (since the services are schema stitched).

1

1 Answers

2
votes

Your question doesn't show any of your existing code for the API gateway, but I'm assuming you are stitching your schemas as described in the docs. Each service's schema should only provide fields that are appropriate to its particular domain. For example, the posts service schema should not include any references to users. When you stitch your schemas together, you then extend the existing types by providing additional fields that link your different services.

const extensions = `
extend type User {
  posts
}

extend type Post {
  user
}
`

const mergedSchema = mergeSchemas({
  schemas: [
    usersSchema,
    postsSchema,
    extensions,
  ],
  resolvers: {
    User: {
      posts: {
        fragment: `... on User { id }`,
        resolve(user, args, context, info) {
          return info.mergeInfo.delegateToSchema({
            schema: postsSchema,
            operation: 'query',
            fieldName: 'posts',
            args: {
              userId: user.id,
            },
            context,
            info,
          });
        },
      },
    },
    Post: {
      user: {
        fragment: `... on Post { userId }`,
        resolve(post, args, context, info) {
          return info.mergeInfo.delegateToSchema({
            schema: postsSchema,
            operation: 'query',
            fieldName: 'user',
            args: {
              id: post.userId,
            },
            context,
            info,
          });
        },
      },
    },
  },
});

Here we're extending the User type from the users service to include a posts field. We use delegateToSchema to indicate that this extra field should actually be resolved by the posts service's schema by querying posts and passing in the user id as a userId argument (the actual field and argument names will need to match your schemas). We similarly add a user field to each Post and delegate resolving that field to the users service schema.

Please check the docs for additional details.