GraphQL field to select data from the database

The way graphql structures the data fetching architecture is simply awesome. While working with graphql we might have the case where we want to get only certain data from the database. The database has some secret credentials like password, keys, auth ids which we don’t want to disclose even to the API.

So we have to get data from the database dynamically that means select database fields dynamically. For example, if the client selects the only username and email, the graphql API has to fetch only those values from the database.

There are packages that do this for us but we need to avoid installing packages for every bit of the problem. We can do it without any sort of the third packages using the apollo server’s info argument on the resolver.

We are using an apollo server here.
const resolvers = {
  Query: {
    user(parent, args, context, info) {}
}

[info] contains information about the operation's execution state, including the field name, the path to the field from the root, and more. - Apollo server docs

We have the client query:

query userDetail($email: String!) {
  getUser(email: $email){
    username
    email
  }
}

The info arg contains the field name that comes from the client. Now we use it to extract the selection field as:

const fields = info.fieldNodes[0].selectionSet.selections;
const attributes = fields.map(field => field.name.value);

console.log(attributes); // ['username', 'email']

We have both username and email. Now we'll fetch data from the database.

sequelize

const user = await User.findOne({
  where: { email },
  attributes,
});

With mongoose,

const user = await User.findOne({ email }).select(attributes);

We can modify the user further according to our needs and return to the client.

Full code

const resolvers = {
  Query: {
    getUser: async (_, args, __, info) => {
      const { email } = args;

      try {
        const fields = info.fieldNodes[0].selectionSet.selections;
        const attributes = fields.map(field => field.name.value);

        const user = await User.findOne({
          where: { email },
          attributes,
        });

        return user;
      } catch (error) {
        throw error;
      }
    },
  },
};

Final Note

The code above is just for the demo purpose. It might not be the right approach for this kind of simple API but for the more advanced or heavy API, it gives a higher performance as the database selects only given attributes.

Database benchmark also shows selecting only certain fields/attributes increases the query performance because the database only needs to make calculations on given fields.

© Bipin