---
# System prepended metadata

title: Top level queries impact on client codebase

---

# Top level queries impact on client codebase

We have recently enabled the option to directly query a number of entities (Collaboration, Context and Community)


Although it may seem that this allows for cleaner codebase in terms of not needing to destruct a deep object structure there is a cost to it. We never use collaboration, context nor community as “independent” entities in the way we use Cards or Canvases.

In order to query them as a top level entity we would need to have their IDs, which means:
    1. Needing to perform a query to get the ID
    2. Perform a query to fetch the entity using the returned ID
    
We would need to move the “ugly” unreadable destructuring code to a parent/container component to be able to pass that ID to a child component to be able to perform a query that returns a flat object. We have not removed the unreadable code, we just moved it and we now have two queries instead of one. ID fetching could be done inside a (Hub/Challenge/Opportunity)Context or via separate context but we are discouraging this type of context usage.

### Tripled query problems

Tripled query problems are when we are re-using components for all three (or in some cases 2) journey types. Depending on which combination of props is passed (hubId!, challengeId?, opportunityId?) we are calling one and skiping the rest of the queries.

This problem is partially connected to the problem of destructuring deep nested objects but can be solved by using @include and @skip directives when writing graphql queries.

This code:
```typescript
const useCallouts = (params: OptionalCoreEntityIds) => {
  // queries
  const { data: hubCalloutsData, loading: hubCalloutsLoading } = useHubCalloutsQuery({
    variables: isHubId(params) ? params : (params as never),
    fetchPolicy: 'cache-and-network',
    skip: !isHubId(params),
  });

  const { data: challengeCalloutsData, loading: challengeCalloutsLoading } = useChallengeCalloutsQuery({
    variables: isChallengeId(params) ? params : (params as never),
    fetchPolicy: 'cache-and-network',
    skip: !isChallengeId(params),
  });

  const { data: opportunityCalloutsData, loading: opportunityCalloutsLoading } = useOpportunityCalloutsQuery({
    variables: isOpportunityId(params) ? params : (params as never),
    fetchPolicy: 'cache-and-network',
    skip: !isOpportunityId(params),
  });

  const [getHubCallouts] = useHubCalloutsLazyQuery({
    variables: isHubId(params) ? params : (params as never),
    fetchPolicy: 'cache-and-network',
  });
  const [getChallengeCallouts] = useChallengeCalloutsLazyQuery({
    variables: isChallengeId(params) ? params : (params as never),
    fetchPolicy: 'cache-and-network',
  });

  const [getOpportunityCallouts] = useOpportunityCalloutsLazyQuery({
    variables: isOpportunityId(params) ? params : (params as never),
    fetchPolicy: 'cache-and-network',
  });

  const collaboration = (
    hubCalloutsData?.hub ??
    challengeCalloutsData?.hub.challenge ??
    opportunityCalloutsData?.hub.opportunity
  )?.collaboration;

  const reloadCallouts = isOpportunityId(params)
    ? getOpportunityCallouts
    : isChallengeId(params)
    ? getChallengeCallouts
    : getHubCallouts;

```

Becomes this code:
```typescript
const useCallouts = (params: OptionalCoreEntityIds) => {
  const includeHub = !(params.challengeNameId || params.opportunityNameId);
  const includeChallenge = !!params.challengeNameId;
  const includeOpportunity = !!params.opportunityNameId;

  const { data: calloutsData, loading: calloutsLoading } = useCalloutsQuery({
    variables: {
      hubNameId: params.hubNameId!,
      challengeNameId: params.challengeNameId,
      opportunityNameId: params.opportunityNameId,
      includeHub,
      includeChallenge,
      includeOpportunity,
    },
    fetchPolicy: 'cache-and-network',
    skip: !params.hubNameId,
  });

  const [getCallouts] = useCalloutsLazyQuery({
    variables: {
      hubNameId: params.hubNameId!,
      challengeNameId: params.challengeNameId,
      opportunityNameId: params.opportunityNameId,
      includeHub,
      includeChallenge,
      includeOpportunity,
    },
    fetchPolicy: 'cache-and-network',
  });

  const collaboration =
    calloutsData?.hub?.collaboration ??
    calloutsData?.hub.challenge?.collaboration ??
    calloutsData?.hub.opportunity?.collaboration;

  const reloadCallouts = getCallouts;

```

List of ***tripled queries*** by entity:

    Collaboration:
        - useAspectSettings
	    - AspectDashboardContainer
	    - AspectProvider
	    - useCalloutCreation
	    - CanvasProvider
	    - useAspectCreatedOnCalloutSubscription
    
    Context:
        - ContributionDetailsContainer
        
    Community:
        None found



Client codebase can be improved for some other entities. One example is when querying a Card or a Canvas. Currently we need to use the following query:

```graphql=
collaboration {
    ...
    callouts(IDs: [calloutNameId]) {
        ...
        aspects(IDs: [aspectNameID]){
            ...
        }
    }
}
```

This means we need to search through two arrays to get the entity we need.
```javascript=
const hubAspect = getCardCallout(hubData?.hub?.collaboration?.callouts, aspectNameId)?.aspects?.find(
    x => x.nameID === aspectNameId
  );
```

We can update our API to add resolver fields that allow searching for a specific callout under collaboration and specific aspect under callout. The query would look like this:
```graphql=
collaboration {
    ...
    callout(ID: calloutNameId) {
        ...
        aspect(ID: aspectNameID){
            ...
        }
    }
}
```

And the extraction code becomes much cleaner:
```javascript=
const hubAspect = hubData?.hub?.collaboration?.callout?.aspect;
```

We created this tech debth when introducing Callouts.
The issue is also present when fetching Canvases. We don't want to build the backend for frontend but this simple API update would ease the destructuring around Cards and Canvases.