Tips for combining data from various sources in GraphQL

Need some guidance on setting up GraphQL as a beginner. I am facing challenges in efficiently setting up resolvers for my schema, especially when dealing with fields from different backend APIs.

type User {
   name: String
   address: String
   dateOfBirth: String
   department: String
   function: String
   manager: String
}

Fields like name, address, and dateOfBirth are retrieved from a basic administration system, while the other fields come from an organizational database.

Query {
  User(parent, args, ctx) {
    return {
       name: '....',
       address: '...',
       dateOfBirth: '.....'
    }
  }
} 

If each subfield is resolved individually, it would result in multiple requests when all fields are requested. To optimize this, I want to structure my schema in a way where similar fields can be fetched together in one API call.

type User {
   name: String
   address: String
   dateOfBirth: String
   organisation: Organisation
} 
type Organisation {
   department: String
   function: String
   manager: String
}

This approach reduces the number of requests made to the Organization API. However, it does make the schema look a bit unusual as these fields should ideally belong to a nested object structure. Additionally, any future changes in the data sources could potentially break the schema.

I experimented with using a dataloader to batch the fields, but it seems more suited for resolving n+1 problems rather than batching different field types.

Looking for suggestions on how to tackle this challenge effectively. Any advice would be greatly appreciated.

Answer №1

In the context of resolving data in GraphQL, whatever is resolved on the parent resolver will be passed down to the child resolvers. For example, in the User resolver, you can simply do:

Query {
  User(parent, args, ctx) {
    //Call your API here that returns the organization fields
    const organization = getOrganizationFields();
    return {
       ...organization,
       name: '....',
       address: '...',
       dateOfBirth: '.....'
    }
  }
} 

Then, in the child resolvers (field resolvers), this information will be accessible and you can retrieve it like so:

User {
   department(parent, args, ctx) {
     return parent.department
   }
   manager(parent, args, ctx) {
     return parent.manager
   }
}

Some implementations like graphql-js handle these "unitary" resolvers implicitly, saving you from having to write them out explicitly. However, if you need to batch or cache API requests, using a DataLoader would be recommended.

Answer №2

Here is a potential schema to consider:

type User {
   name: String
   address: String
   dateOfBirth: String
   organisation: Organisation
} 
type Organisation {
   department: String
   function: String
   manager: String
}

These are my resolvers for the subfield:

User {
   organisation {

      // retrieve data from organisation service using parent.id
      
      return {
         department: '...',
         function: '...'
         manager: '...'
      }
   }
} 

The request to the Organisation API is now only made once.

You're making good progress with GraphQL where you can request only necessary information.

Albert's [user level resolver] observation about overfetching may be correct with a single datasource (one SQL request with joins).

Nevertheless, the schema seems odd: those fields should belong to a sub-object.

True, but there are ways to resolve it in this manner.

If we were to separate manager data into its own API, changing the schema would cause issues if it's taken out of the sub-object Organisation.

Yes... you probably need a more structured schema.

... you might be:

  • considering this schema from an existing service/BE perspective;
  • adding the complexity of breaking changes/API versioning to this context;

... however, you can already define 'the right schema', for example

type User {
  name: String
  address: String
  dateOfBirth: String
  role: Role
} 
type Role {
  organisation: Organisation
  function: String
}
type Organisation {
  department: String
  manager: User

}

... assuming only one role per user ... use array otherwise

In this scenario, role and organisation subobject can be resolved with just one call to the organisation service - simple

return {
  function: "team lead",
  organisation: {
    id: "someDeptID",
    department: "some",
  }
}

...if your query requires the manager field, then it should (must) be resolved by the Organisation.manager resolver. Of course, you can also handle it there (in the role resolver) if the data has already been fetched/available.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to retrieve a list of customers within a specified date range?

How can I retrieve a list of customers within a specified date range? My frontend react app includes starting and ending date pickers, but I'm unsure how to query the data using mongoose in my express app. Below you'll find my mongoose model and ...

What is the best method for converting IDs into objects within ng-options in Angular?

Is there a way to dynamically use an array of IDs as the source of my ng-option directive inside of select? Instead of creating an array of objects with corresponding IDs, I am wondering if there is a method to set a function as the source of ng-option. ...

JavaScript, AJAX rapid iteration

I am working with some ajax code: $(document).ready( function() { $("#button1").click( function() { $.ajax({ type: "POST", url: "update.php", }); }); }); In my HTML code, I have 200 buttons. How can I ...

Transform nested properties of an object into a new data type

I created a versatile function that recursively converts nested property values into numbers: type CastToNumber<T> = T extends string ? number : { [K in keyof T]: CastToNumber<T[K]> }; type StringMap = { [key: string]: any }; const castOb ...

Is the HTML5 type of button functioning properly, while the type of submit is not working as

Looking to validate a form before running a JavaScript function? Check out this code snippet: function updateMap() { //dummy } <form> <div class="group"> <input type="number" id="hour" min="0" max="23" required> <span cl ...

The PHP script's header() function is failing to execute

Recently, I encountered an issue with my JavaScript code that calls a backend PHP script using AJAX. The main function of the AJAX request is to send user login data (username and password) to the PHP script, which in turn queries this information on the S ...

Tips for Running a Unique Code the First Time the $.each() Function is Used

During the initial iteration of my $.each() loop, I run a unique code. However, for all subsequent iterations until the end of the loop, my code remains the same. ...

Infinite scrolling with a dynamic background

Hi there, I am working on my website and trying to create a smooth transition between sections similar to the one demonstrated here:. The challenge I'm facing is that the backgrounds of my sections cannot be fixed; they need to have background-attachm ...

Successfully submitting form_with without experiencing any errors for mandatory fields

I am currently dealing with a form that triggers a sidekiq worker upon submission. The issue I am facing is that even if the form is empty, clicking submit still attempts to run the worker. What I would like is for an alert to notify the user that the fiel ...

I am facing an issue with uploading files to my designated directory through Node.js Multer

After creating a web service using node js and implementing a form with React interface that includes user information and file upload, I encountered an issue while attempting to save the file to the specified directory on the node js server using multer. ...

Ensure that a function completes before moving on in JavaScript

I am attempting to modify the save method so that it waits for this.collection.create() to complete before running, in order to prevent a potential crash. class UserRepository extends BaseRepository<User> { constructor() { super(); ...

The Heroku system encountered an issue: ENOENT - file or directory not found, trying to access '.env'

I'm encountering issues while attempting to deploy my application to Heroku: Error: ENOENT: no such file or directory, open '.env' 2019-04-10T01:38:23.050188+00:00 app[web.1]: 1 at Object.openSync (fs.js:438:3) 2019-04-10T01:38:23 ...

Top method for utilizing overlays

Is there a method to randomly select hex codes for specific colors? I want to replicate the design in this image through coding. Image Here is the code I have so far: HTML <div id="group"> <div class="sub red panel"> </div><!--su ...

Receiving a notification when attempting to log in with incorrect credentials

I am currently working on an Angular login page implementation using a username and password setup. When the user enters incorrect credentials, I want to display an alert message indicating the same. Here is the HTML code snippet for the form: <form [f ...

Filtering JSON data in AngularJS is simple and effective

I am working with JSON data and I want to display it filtered by genre. The solution provided in the previous question How to filter JSON-Data with AngularJs? did not work for me. Here is myapp.js: var myApp = angular.module('myApp', []); myAp ...

Assign a value to an Html.HiddenField without tying it to the model upon form submission

I have two classes, SubsidiaryClient and Client, that are related in a one-to-many manner as depicted below. Currently, I am utilizing MVC 5 for development. In the Create SubsidiaryClient page, to retrieve the ClientID, you need to click on the browse bu ...

Associating functions with events within objects

I am encountering a unique issue with jQuery and JavaScript in general. Currently, I am developing a JS file that allows me to create portlets for a client's website. I am utilizing SignalR to send updates to the users. The code snippet below is cau ...

What is preventing me from executing this function more than once?

Having this function: const sliderTextChange = document.getElementsByClassName('slider') // text change const changeSliderText = change => { const sliderLeft = document.getElementsByClassName('switch-left') const sliderRight = ...

Are You Able to Develop a Floating Window That Stays Persistent in JavaScript?

Looking to create a persistent floating window in a NextJS 14 app. This window needs to remain on top, even when navigating between browser windows. Want it to stay visible even when the browser window is minimized, like a Picture-In-Picture feature. Mos ...

Employing an object from a distinct module

After creating a function to parse objects and provide getters, I encountered an issue. I need to access this object from a different module without re-parsing it each time. Is there a way to achieve this without using a global variable? var ymlParser = r ...