GraphQL in Salesforce LWC

I am providing practical example of GraphQL with LWC. In this article I will explain below points

# Get Data from Salesforce Object using GrapghQL

# Passing Dynamic value as parameter in GraphQL where clause

# Retrieve Multiple object(Parent and Child) data in Single GraphQL

# Limit on data retrieve, Order by Clause, And, OR, Not operator in where Clause of GraphQL.

# Passing Parameter to Child Component and display child component details in model popup

Here I am considering example of Account, Contact, Opportunity. In first Page I will show high level account info list and when I click on any account then second page will show Account , Related Opportunity and Related Contacts.

 

Final Output Screen As Below

Create two custom label as shown in below screen

Step 1. Create ad_Account_Info LWC Component and copy paste below code for html, js, css and meta file.

# ad_Account_Info.html

<template>
	<template if:true={isAccountInfo}>
		<lightning-card>
			<h1 slot="title">
				<lightning-icon icon-name="custom:custom18" size="medium"></lightning-icon>
				&nbsp;&nbsp;Account Info
			</h1>
		</lightning-card>		
		<div class="slds-p-around_small slds-theme_default">
			<lightning-tabset>				
				<lightning-tab label={masterResultsSize}>					
					<lightning-layout>
						<lightning-layout-item size="12" data-item="inlineblock">							
							<template if:true={results}>
								<template for:each={results} for:item="account">
									<div key={account.Id}>										
										<div class="slds-m-around_medium slds-box slds-theme_default">
											<table>
												<tr onclick={navigateToAccountDetail} data-item={account.Id}>
													<th data-item={account.Id}>
														<lightning-icon data-item={account.Id}
															icon-name="standard:task" size="small">
														</lightning-icon>
														&nbsp;
														{account.Name}
														<b data-item={account.Id} class="statussyle">{account.Account_Status__c}</b>
													</th>
												</tr>
												<tr  onclick={navigateToAccountDetail}>
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Country:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.Country__c}</span>
													</td>
												</tr>
												<tr  onclick={navigateToAccountDetail}>
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Account Number:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.AccountNumber}</span>
													</td>
												</tr>
												<tr  onclick={navigateToAccountDetail}>
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Account Source:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.AccountSource}</span>
													</td>
												</tr>
												<!--SH-1299 : added Account# Start -->
												<tr  onclick={navigateToAccountDetail}>
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Account Email:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.AccountEmail__c}</span>
													</td>
												</tr>												
											</table>
										</div>
										<!-- </lightning-card> c -->
									</div>
								</template>
							</template>
						</lightning-layout-item>
					</lightning-layout>
				</lightning-tab>
				<lightning-tab label="Not Started (0)">					
					<lightning-layout>
						<lightning-layout-item size="12" data-item="inlineblock">							
							<template if:true={results}>
								<template for:each={results} for:item="account">
									<div key={account.Id}>										
										<div class="slds-m-around_medium slds-box slds-theme_default">
											
										</div>
										<!-- </lightning-card> c -->
									</div>
								</template>
							</template>
						</lightning-layout-item>
					</lightning-layout>
				</lightning-tab>
			</lightning-tabset>
		</div>
	</template>

	<template if:true={isAccountDetailData}>
		<c-ad_-account_-details record-id={accountid} onclose={handleCancelModalClose} account-id={accountid}>
		</c-ad_-account_-details>
	</template>
	<!-- Spinner Code -->
	<div class="spinner">
		<template if:true={isLoading}>
			<lightning-spinner alternative-text="Loading" variant="brand" size="large">
			</lightning-spinner>
		</template>
	</div>
	<!-- Spinner Code Complete -->
</template>

# ad_Account_Info.js

import { LightningElement, wire, api } from "lwc";
import { gql, graphql } from "lightning/uiGraphQLApi";
import LightningAlert from 'lightning/alert';
import AD_Account_Status from "@salesforce/label/c.AD_Account_Status";
import AD_Opportunity_Stage from "@salesforce/label/c.AD_Opportunity_Stage";

export default class Ad_Account_Info extends LightningElement {   //Get all the custom labels from sh_FIM_Constants LWC Js
  
     
    @api isAccountDetailData=false;
    isAccountInfo = true;
    accountid;
    isLoading = false;   
    results;
    masterResults;
    masterResultsSize;
    errors;
    accountStatus = AD_Account_Status.split(",");//In Progress,Not Started,On Hold,Complete
    oppStage = AD_Opportunity_Stage.split(",");// Prospecting,Qualification,Closed Won,Closed Lost;
   
     connectedCallback() {
         this.isLoading = true;
     }
    // Clickable cycle count card to open scan product page..
    navigateToAccountDetail(event) {
        this.accountid = event.target.getAttribute('data-item');
        this.isAccountDetailData = true;     
    }
     
    @wire(graphql, {
        query: gql`
      query getAccountList($accountStatus: [Picklist]){
        uiapi {
          query {
            Account (
                first:100
                where: { 
                    Account_Status__c: { in:   $accountStatus  }
                    Active__c: { eq: "Yes" }
                    Name: { like: "Avinash%" } 
                    } 
                orderBy: {  
                    Name: { order: ASC } 
                    }
                ) 
                {
              edges {
                node {
                    Id
                    Name{value}
                    AccountNumber{value}
                    AccountSource{value}                                                        
                    Country__c{value}
                    Account_Status__c{value}  
                    AccountEmail__c{value}
                }
              }
            }                     
         }
      }
    }`,
    variables: '$myVariables',
    operationName: 'getAccountList',
    })
    graphqlQueryResult({ data, errors }) {
        if (data) {
            this.results = data.uiapi.query.Account.edges.map((edge) => ({
                Id: edge.node.Id,
                Name: edge.node.Name.value,               
                Account_Status__c: edge.node.Account_Status__c.value,
                Country__c: edge.node.Country__c.value,
                AccountNumber: edge.node.AccountNumber.value,
                AccountSource: edge.node.AccountSource.value,
                AccountEmail__c: edge.node.AccountEmail__c.value,
            }));
            this.masterResults = this.results;                    
            this.errors = errors;
            this.isLoading = false; 
            this.masterResultsSize ='In Progress ('+ Object.keys(this.masterResults).length+')';     
        }
        
    }

 get myVariables() {
      return {          
          accountStatus: this.accountStatus
      };
  }


finalResult;
get finalRes(){
    this.finalResult=this.results;
    console.log('finalRes() -- '+ this.results);
    return this.masterResults;
}

 handleCancelModalClose() {
    this.isAccountDetailData = false;
 }

 /* Below are example of where clause in GraphQL
    # where: { Id: { eq: "001xx000003GYQxAAO" } };
    # where: { Id: { in: [ "001xx000003GYQxAAO", "001xx000003GYQxAA1" ] } };
    # where: { NextStep: { ne: null } };
    # where: { Name: {like: "%erv" } };
    # where: { SchedStartTime: { gte: { range: { last_n_months: 4 } } } }
    # where: { CreatedDate: { lte: { range: { n_days_ago: 2 } } } }
    # where: { CreatedDate: { eq: { literal: TODAY } } }
    # where: { CreatedDate : { CALENDAR_YEAR: { value: { eq: 2022 } } } }
    # where: { CreatedDate : { CALENDAR_YEAR: { value: { eq: 2022 } } } }
    # where: { LastModifiedDate: { gte: { value: "2022-06-12T03:29:56.901Z"} } }
    # where: { AnnualRevenue: { gt: 10000, lt: 100000 } }
    # Case (orderBy: { Account: { Name: { order: ASC } } } ) {
    # Contact (where: {
        or: [
            { LastName: { eq: "Young" } },
            { Account : { Name: { eq: "Edge Communications" } } }
          ]
    })
    # Case (where: { Contact: { LastName: { eq: null } } } )
    Opportunity(
    where: {
      or: [
        {
          Probability: { eq: 100 },
          StageName: { eq: "Closed Won" }
        },
        {
          Type: { eq: "New Customer" },
          ExpectedRevenue: { gt: 1000000 }
        }
      ]
    }
   )
    # where: {
    not: {
        ShippingCity: {
        eq: { "Tokyo" }
        }
    }
    }
    # Opportunity(
    where: {
    not: { Probability: { eq: 100 }
            StageName: { eq: "Closed Won" }
    }
    }
    )
    # Opportunity(
    where: {
        or: [
        {
            Probability: { eq: 100 },
            StageName: { eq: "Closed Won" }
        },
        {
            Type: { eq: "New Customer" },
            ExpectedRevenue: { gt: 1000000 }
        }
        ]
    }
    )
        */
        async handleAlertClick(msg) {
        await LightningAlert.open({
            message: '   this.accountid => ' +  msg,
            theme: 'error', // a red theme intended for error states
            label: 'Error!', // this is the header text
        });
        //Alert has been closed
    }  
  
}

# ad_Account_Info.css

.dummy{
	color : black;
}
.pointer{
  cursor:pointer;
}
.header{
	padding-left: var(--lwc-spacingSmall);
    font-weight: var(--lwc-fontWeightLight);
	font-size: 170%;
    color: var(--lwc-colorTextDefault);
    vertical-align: middle;
}
.capital{
	text-transform: capitalize;
}
.iconsize{
	vertical-align: middle;
	
}
.labelcolor{
	color: #009cde;
}
.slds-size_1-of-2{
	
	--sds-c-card-color-background: #009cde;
	--sds-c-card-color-background: #009cde;
	
}
.backcolor{
	background-color: #009cde;
}
.lablelineitemstyle{
	float:left;
}
.statussyle{
  float: right;
}
.scrolling{
	height:50rem;
}

.shadow {
	box-shadow: 1px 1px 5px 3px grey;
	text-align: center;
	padding:2%;
	color:blue;
	font-size: 1em;
}

.labelHeading{
	font-size: 1.5em;
	padding-left: 1%;
	vertical-align: middle;
}

# ad_Account_Info.js-meta.xml

<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
	<apiVersion>57.0</apiVersion>
	<isExposed>true</isExposed>
	<targets>
		<target>lightning__RecordPage</target>
		<target>lightning__AppPage</target>
		<target>lightning__HomePage</target>
		<target>lightning__Tab</target>
		<target>lightning__RecordAction</target>
	</targets>
</LightningComponentBundle>

Step 2 : Create another LWC component ad_Account_Details and copy paste below code for html, js, css and meta file.

# ad_Account_Details.html

<template>
    <!-- Modal/Popup Box LWC starts here -->
    <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true"
        aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
        <div class="slds-modal__container">
            <!-- Modal/Popup Box LWC header here -->
            <header class="slds-modal__header">
                <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close"
                    onclick={closeModal}>
                    <lightning-icon icon-name="utility:close" alternative-text="close" variant="inverse" size="small">
                    </lightning-icon>
                    <span class="slds-assistive-text">{adClose}</span>
                </button>
                <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">
                    {adAccountDetails}</h2>
            </header>
            <!-- Modal/Popup Box LWC body starts here -->
            <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
                <!--SPINNER-->
                <template if:true={isSpinner}>
                    <div class="slds-is-relative">
                        <lightning-spinner alternative-text="Loading..." size="medium"></lightning-spinner>
                    </div>
                </template>
                <!--SPINNER END-->
                
              <template if:true={results}>
				  <div><b>Account:</b></div>
								<template for:each={results} for:item="account">
									<div key={account.Id}>										
										<div class="slds-m-around_medium slds-box slds-theme_default">
											<table>
												<tr  data-item={account.Id}>
													<th data-item={account.Id}>
														<lightning-icon data-item={account.Id}
															icon-name="standard:task" size="small">
														</lightning-icon>
														&nbsp;
														{account.Name}
														<b data-item={account.Id} class="statussyle">{account.Account_Status__c}</b>
													</th>
												</tr>
												<tr  >
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Country:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.Country__c}</span>
													</td>
												</tr>
												<tr >
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Account Number:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.AccountNumber}</span>
													</td>
												</tr>
												<tr>
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Account Source:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.AccountSource}</span>
													</td>
												</tr>
												<!--SH-1299 : added Account# Start -->
												<tr>
													<td data-item={account.Id}>
														<b data-item={account.Id} class="lablelineitemstyle">Account Email:&nbsp;</b>&nbsp;
														<span data-item={account.Id} class="lablelineitemstyle">{account.AccountEmail__c}</span>
													</td>
												</tr>												
											</table>
										</div>
										<!-- </lightning-card> c -->
									</div>
								</template>
							</template>

                            <template if:true={oppresults}>
                                <div><b>Opportunity:</b></div>
								<template for:each={oppresults} for:item="opp">
									<div key={opp.Id}>										
										<div class="slds-m-around_medium slds-box slds-theme_default">
											<table>
												<tr  data-item={opp.Id}>
													<th data-item={opp.Id}>
														<lightning-icon data-item={opp.Id}
															icon-name="standard:opportunity" size="small">
														</lightning-icon>
														&nbsp;
														{opp.Name}
														<b data-item={opp.Id} class="statussyle">{opp.StageName}</b>
													</th>
												</tr>
												<tr>
													<td data-item={opp.Id}>
														<b data-item={opp.Id} class="lablelineitemstyle">Closed Date:&nbsp;</b>&nbsp;
														<span data-item={opp.Id} class="lablelineitemstyle">{opp.CloseDate}</span>
													</td>
												</tr>
												<tr>
													<td data-item={opp.Id}>
														<b data-item={opp.Id} class="lablelineitemstyle">Probability:&nbsp;</b>&nbsp;
														<span data-item={opp.Id} class="lablelineitemstyle">{opp.Probability}</span>
													</td>
												</tr>
																							
											</table>
										</div>
										<!-- </lightning-card> c -->
									</div>
								</template>
							</template>
					 <template if:true={contresults}>
                                <div><b>Contact:</b></div>
								<template for:each={contresults} for:item="con">
									<div key={con.Id}>										
										<div class="slds-m-around_medium slds-box slds-theme_default">
											<table>
												<tr  data-item={con.Id}>
													<th data-item={con.Id}>
														<lightning-icon data-item={con.Id}
															icon-name="standard:contact" size="small">
														</lightning-icon>
														&nbsp;
														{con.Name}
														
													</th>
												</tr>
												<tr>
													<td data-item={con.Id}>
														<b data-item={con.Id} class="lablelineitemstyle">Title:&nbsp;</b>&nbsp;
														<span data-item={con.Id} class="lablelineitemstyle">{con.Title}</span>
													</td>
												</tr>
												<tr>
													<td data-item={con.Id}>
														<b data-item={con.Id} class="lablelineitemstyle">Email:&nbsp;</b>&nbsp;
														<span data-item={con.Id} class="lablelineitemstyle">{con.Email}</span>
													</td>
												</tr>
												<tr>
													<td data-item={con.Id}>
														<b data-item={con.Id} class="lablelineitemstyle">Phone:&nbsp;</b>&nbsp;
														<span data-item={con.Id} class="lablelineitemstyle">{con.Phone}</span>
													</td>
												</tr>
																							
											</table>
										</div>
										<!-- </lightning-card> c -->
									</div>
								</template>
							</template>
          

                <!--ADDS SPACE AT THE BOTTOM OF THE PAGE-->
                <lightning-layout class="slds-p-bottom_small">
                    <lightning-layout-item size="4" padding="around-large">
                    </lightning-layout-item>
                </lightning-layout>
                <!--ADDS SPACE AT THE BOTTOM OF THE PAGE END-->

            </div>
            <!-- Modal/Popup Box LWC footer starts here -->
            <footer class="slds-modal__footer">
                <button class="slds-button slds-button_neutral" onclick={closeModal}
                    title={adCancel}>{adCancel}</button>
                
            </footer>
        </div>
    </section>
    <div class="slds-backdrop slds-backdrop_open"></div>
</template>

# ad_Account_Details.js

import { LightningElement,wire, api } from 'lwc';
import { gql, graphql } from "lightning/uiGraphQLApi";
import LightningAlert from 'lightning/alert';
import AD_Account_Status from "@salesforce/label/c.AD_Account_Status";
import AD_Opportunity_Stage from "@salesforce/label/c.AD_Opportunity_Stage";

export default class Ad_Account_Details extends LightningElement {
    @api accountId;
    adClose = 'Close';
    adAccountDetails ='Account Details New';
    isSpinner=false;
    adCancel='Cancel';

    results;
    oppresults;
    contresults;

    accountStatus = AD_Account_Status.split(",");//In Progress,Not Started,On Hold,Complete
    oppStage = AD_Opportunity_Stage.split(",");// Prospecting,Qualification,Closed Won,Closed Lost;
   

     //closes modal popup
    closeModal() {
        this.dispatchEvent(new CustomEvent('close', { detail: { value: false } }));
    }


    @wire(graphql, {
        query: gql`
      query getAccountOppContactDetails($accountId:ID, $accountStatus: [Picklist], $oppStage: [Picklist]){
        uiapi {
          query {
            Account (               
                where: { 
                    Id: { eq: $accountId}
                    Account_Status__c: { in:   $accountStatus  }
                    Active__c: { eq: "Yes" }
                    Name: { like: "Avinash%" } 
                    } 
                orderBy: {  
                    Name: { order: ASC } 
                    }
                ) 
                {
              edges {
                node {
                    Id
                    Name{value}
                    AccountNumber{value}
                    AccountSource{value}                                                        
                    Country__c{value}
                    Account_Status__c{value}  
                    AccountEmail__c{value}
                }
              }
            }  
            Opportunity(
                first:100
                where: { 
                    AccountId: { eq: $accountId} 
                    StageName: { in:   $oppStage  }                    
                    } 
                orderBy: {  
                    Name: { order: ASC } 
                    }
            )  
            {
              edges {
                node {
                    Id
                    Name{value}
                    CloseDate{value}
                    Probability{value}
                    }
                    }
                    }
             Contact(
                first:100
                where: { 
                    AccountId: { eq: $accountId} 
                    } 
                orderBy: {  
                    Name: { order: ASC } 
                    }
            )  
            {
              edges {
                node {
                    Id
                    Name{value}
                    Title{value}
                    Email{value}
                    Phone{value}
                    }
                    }
                    }

         }
      }
    }`,
    variables: '$myVariables',
    operationName: 'getAccountOppContactDetails',
    })
    graphqlQueryResult({ data, errors }) {
        if (data) {
            this.results = data.uiapi.query.Account.edges.map((edge) => ({
                Id: edge.node.Id,
                Name: edge.node.Name.value,               
                Account_Status__c: edge.node.Account_Status__c.value,
                Country__c: edge.node.Country__c.value,
                AccountNumber: edge.node.AccountNumber.value,
                AccountSource: edge.node.AccountSource.value,
                AccountEmail__c: edge.node.AccountEmail__c.value,
            }));
           // this.masterResults = this.results;    
           console.log('==='+this.results); 
            this.oppresults = data.uiapi.query.Opportunity.edges.map((edge) => ({
                Id: edge.node.Id,
                Name: edge.node.Name.value,               
                CloseDate: edge.node.CloseDate.value,
                Probability: edge.node.Probability.value,              
            }));
              console.log('==='+this.oppresults); 
            this.contresults = data.uiapi.query.Contact.edges.map((edge) => ({
                Id: edge.node.Id,
                Name: edge.node.Name.value,               
                Title: edge.node.Title.value,
                Email: edge.node.Email.value,   
                Phone: edge.node.Phone.value,             
            }));  console.log('==='+this.contresults); 

            this.errors = errors;
            this.isLoading = false; 
            //this.masterResultsSize ='In Progress ('+ Object.keys(this.results).length+')'; 

        }
        
    }

 get myVariables() {
      return {          
          accountId: this.accountId,
          accountStatus: this.accountStatus,
          oppStage: this.oppStage
      };
  }
  async handleAlertClick(msg) {
        await LightningAlert.open({
            message: '   this.accountid => ' +  msg,
            theme: 'error', // a red theme intended for error states
            label: 'Error!', // this is the header text
        });
  }

   displayToast(title, message, variant, mode = 'dismissable') {
   const evt = new ShowToastEvent({
      title: title,
      message: message,
      variant: variant,
      mode: mode

   });
   return evt;
}


}

# ad_Account_Details.css

.dummy{
	color : black;
}
.pointer{
  cursor:pointer;
}
.header{
	padding-left: var(--lwc-spacingSmall);
    font-weight: var(--lwc-fontWeightLight);
	font-size: 170%;
    color: var(--lwc-colorTextDefault);
    vertical-align: middle;
}
.capital{
	text-transform: capitalize;
}
.iconsize{
	vertical-align: middle;
	
}
.labelcolor{
	color: #009cde;
}
.slds-size_1-of-2{
	
	--sds-c-card-color-background: #009cde;
	--sds-c-card-color-background: #009cde;
	
}
.backcolor{
	background-color: #009cde;
}
.lablelineitemstyle{
	float:left;
}
.statussyle{
  float: right;
}
.scrolling{
	height:50rem;
}

.shadow {
	box-shadow: 1px 1px 5px 3px grey;
	text-align: center;
	padding:2%;
	color:blue;
	font-size: 1em;
}

.labelHeading{
	font-size: 1.5em;
	padding-left: 1%;
	vertical-align: middle;
}

# ad_Account_Details.js-meta.xml

<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
	<apiVersion>57.0</apiVersion>
	<isExposed>true</isExposed>
</LightningComponentBundle>

Setup Lightning Quick Action on Account Object, refer below screen