Skip to main content

GraphQL APIs Customize

Low-Code Customize & Extend - GraphQL APIs

tip

For corresponding details, Please refer to Resources page for locating Github and Videos Learn, Videos, Tutorials

Backend (BE): Low-Code Customize & Extend

This section contains guides to low-code customize GraphQL APIs.

A] GraphQL Low-Code Customize For Beginners

  • Implement Nested Objects
  • Implement Foreign-Key or any other Relationship
  • Customize SelectWhere For specific FindByColumn

B] GraphQL Low-Code Customize For Advanced

  • Implement DataMesh with data from repository or other endpoints REST, GraphQL
  • Implement Custom Business Logic
  • Implement Serverless Cloud Functions

Customize - How Tos ?

Implement Nested Objects

Implementing Nested Objects is easy. Configure Table-to-Table Joins and Multi-Table Relations. Generate code. Complete use case via Low-Code Customize.

Old documentation on Implement Nested Objects

tip

Now code generated for all below.

If you would like to implement Nested Objects fully via Low-Code, please see details below.

Below steps explain customizing to Add nested Inventories data to Product.

Existing Schemas For Product and Inventory

ErpproductTblRecSchema.graphqls

type ErpproductTblRec { 
productId: Int
productName: String
productCategory: String
primarySupplier: String
productDesc: String
productPicture: String
}

ErpinventoryTblRecSchema.graphqls

type ErpinventoryTblRec { 
invId: Int
productId: Int
invDate: EmDate
invQty: Int
invMinQty: Int
invCost: Float
invLocation: String
}

Customize to Implement New Nested Schema and Query

Step 1 : New Schema type & Query

Add To Product schema file ErpproductTblRecSchema.graphqls below contents:

type ErpproductAndInvetoriesTblRec {

productId: Int
productName: String
productCategory: String
primarySupplier: String
productDesc: String
productPicture: String
productAddedColumn: String
erpinventoryTblRecList: [ErpinventoryTblRec]
}

extend type Query {
ErpproductAndInvetoriesTblRecQuery(productId: Int): [ErpproductAndInvetoriesTblRec]!
}

Step 2 : GraphQL Spring Java Code

Step 2-A : Java Model

Locate Java model file for Product and copy it / name it as per new Combined schema model required:

springGql\src\addons\Erpproduct\spring\src\ErpproductTblRec.java COPY TO -> springGql\src\addons\Erpproduct\spring\src\ErpproductAndInvetoriesTblRec.java

Edit ErpproductAndInvetoriesTblRec.java as per below, adding nested Inventory model:

See Edited ErpproductAndInvetoriesTblRec.java

package emrest.spring; 

import java.util.*;
import com.fasterxml.jackson.annotation.JsonFormat;

import emrest.spring.ErpproductTblRec;
import emrest.spring.ErpinventoryTblRec;

public class ErpproductAndInvetoriesTblRec {

private long productId;
private String productName;
private String productCategory;
private String primarySupplier;
private String productDesc;
private byte[] productPicture;
private ErpinventoryTblRec[] erpinventoryTblRecList;

public ErpproductAndInvetoriesTblRec() {

}

public ErpproductAndInvetoriesTblRec(ErpproductTblRec element) {

this.productId = element.getProductId();
this.productName = element.getProductName();
this.productCategory = element.getProductCategory();
this.primarySupplier = element.getPrimarySupplier();
this.productDesc = element.getProductDesc();
this.productPicture = element.getProductPicture();

}


public long getProductId() { return this.productId; }
public String getProductName() { return this.productName; }
public String getProductCategory() { return this.productCategory; }
public String getPrimarySupplier() { return this.primarySupplier; }
public String getProductDesc() { return this.productDesc; }
public byte[] getProductPicture() { return this.productPicture; }

public ErpinventoryTblRec[] getErpinventoryTblRecList() { return erpinventoryTblRecList; };

public void setProductId(long productId ) { this.productId = productId; }
public void setProductName(String productName ) { this.productName = productName; }
public void setProductCategory(String productCategory ) { this.productCategory = productCategory; }
public void setPrimarySupplier(String primarySupplier ) { this.primarySupplier = primarySupplier; }
public void setProductDesc(String productDesc ) { this.productDesc = productDesc; }
public void setProductPicture(byte[] productPicture ) { this.productPicture = productPicture; }

public void setErpinventoryTblRecList(ErpinventoryTblRec[] erpinventoryTblRecList) {
this.erpinventoryTblRecList = erpinventoryTblRecList;
}

}

Step 2-B : GraphQL Resolver

Locate Java GraphQL resolver file for Product and customize it as per below, for adding resolver for Query for new Combined schema model. Refer to Java GraphQL resolver file for Inventory too.

Locate and use below files:

  • springGql\src\addons\Erpproduct\springGraphql\src\ErpproductTblRecGraphqlController.java
  • springGql\src\addons\Erpinventory\springGraphql\src\ErpinventoryTblRecGraphqlController.java

Edit ErpproductTblRecGraphqlController.java as per below adding nested Inventory model:

Add to Product Controller, (Referring to Inventory Controller) -

1) Additional imports for new combined model and inventory model / repository

import emrest.spring.ErpproductAndInvetoriesTblRec;
import emrest.spring.ErpinventoryTblRec;
import emrest.spring.ErpinventoryTblRecRepository;

2) Add Inventory Repository Instance

    @Autowired
ErpinventoryTblRecRepository ErpinventoryTblRec1Repository;

3) Copy existing resolver for product query and rename it for, new required query resolver

Existing :

See Here

// -------------------- Query ------------------------- 

@QueryMapping
//@PreAuthorize("hasRole('USER')")
public List<ErpproductTblRec> ErpproductTblRecQuery(@Argument("productId") long productId)
throws Exception
{
List<ErpproductTblRec> ErpproductTblRecList = new ArrayList<ErpproductTblRec>();
try {

//ErpproductTblRec1Repository.findAll().forEach(ErpproductTblRecList::add);
ErpproductTblRec1Repository.findByProductId(productId).forEach(ErpproductTblRecList::add);

// if (ErpproductTblRecList.isEmpty()) {
// }

} catch (Exception e) {
System.out.println("Error: Exception: "+e.getMessage());
//e.printStackTrace(System.out);
throw new Exception(e.getMessage());
}
return ErpproductTblRecList;
}

Copy and Renamed :

// -------------------- Query Customized ------------------------- 

@QueryMapping
//@PreAuthorize("hasRole('USER')")
public List<ErpproductAndInvetoriesTblRec> ErpproductAndInvetoriesTblRecQuery(@Argument("productId") long productId)
...

4) Edit Copied function further as per below:

tip

Refer to below functions to customize and complete new resolver needed:

  • ErpproductTblRecQuery from ErpproductTblRecGraphqlController.java
  • ErpinventoryTblRecSelectWhere from ErpinventoryTblRecGraphqlController.java

Edited Completed Function Resolver given below :

See Here

// -------------------- Query Customized -------------------------

@QueryMapping
//@PreAuthorize("hasRole('USER')")
public List<ErpproductAndInvetoriesTblRec> ErpproductAndInvetoriesTblRecQuery(@Argument("productId") long productId)
throws Exception
{
//declare new combined model variable
List<ErpproductAndInvetoriesTblRec> ErpproductAndInvetoriesTblRecList = new ArrayList<ErpproductAndInvetoriesTblRec>();
List<ErpproductTblRec> ErpproductTblRecList = new ArrayList<ErpproductTblRec>();
try {

ErpproductTblRec1Repository.findByProductId(productId).forEach(ErpproductTblRecList::add);

//loop thru parent records and add child records
for (ErpproductTblRec element : ErpproductTblRecList) {

List<ErpinventoryTblRec> ErpinventoryTblRecList = new ArrayList<ErpinventoryTblRec>();

//Use SelectWhere method part for getting child data
String searchBy = " productId = " + element.getProductId() + " ";
ErpinventoryTblRecPredicatesBuilder builder = new ErpinventoryTblRecPredicatesBuilder(searchBy);
BooleanExpression queryExpr = builder.build();

ErpinventoryTblRec1Repository.findAll(queryExpr, Pageable.unpaged()).forEach(ErpinventoryTblRecList::add);

//Compose combined data object and add to Parent List
ErpproductAndInvetoriesTblRec ErpproductAndInvetoriesTblRec = new ErpproductAndInvetoriesTblRec(element);
ErpproductAndInvetoriesTblRec.setErpinventoryTblRecList(ErpinventoryTblRecList.toArray(new ErpinventoryTblRec[0]));
ErpproductAndInvetoriesTblRecList.add(ErpproductAndInvetoriesTblRec);
}

} catch (Exception e) {
System.out.println("Error: Exception: "+e.getMessage());
//e.printStackTrace(System.out);
throw new Exception(e.getMessage());
}
return ErpproductAndInvetoriesTblRecList;
}

5) Maven Project - Stop, Clean, Pacakge and Re-Run.

Try New Nested Object Query via client

Query:

{
ErpproductAndInvetoriesTblRecQuery(productId: 1) {
productId
productName
productDesc
productCategory
primarySupplier
erpinventoryTblRecList {
invId
productId
invQty
invDate
invCost
invLocation
}
}
}

Response Data:

{
"data": {
"ErpproductAndInvetoriesTblRecQuery": [
{
"productId": 1,
"productName": "Amoxicillin",
"productDesc": "",
"productCategory": "Antibiotics",
"primarySupplier": "Lupin",
"erpinventoryTblRecList": [
{
"invId": 1,
"productId": 1,
"invQty": 10,
"invDate": "2022-11-28",
"invCost": 50,
"invLocation": "USA"
},
{
"invId": 42,
"productId": 1,
"invQty": 20,
"invDate": "2023-02-19",
"invCost": 100,
"invLocation": "USA"
},
{
"invId": 43,
"productId": 1,
"invQty": 40,
"invDate": "2023-02-18",
"invCost": 200,
"invLocation": "USA"
}
]
}
]
}
}

Implement Foreign-Key or any other Relationship

  • There is simple way to create a database view combining table data with foreign key name/description field. And generating code on it.
  • Also you can Low-Code Customize to add nested object. Please refer to Implement Nested Objects above. For Foreign-Key implementation Use Parent:Child 1:1 Nested object relationship variation.

Implement Data Federation | Data Mesh

Create Data Federation | Data Mesh with any custom combination, for all of your data.

Provide a unified GraphQL API endpoint that includes data from any of:

  • A. GraphQL API query resolver
  • B. GraphQL API endpoint
  • C. REST API endpoint

A default DataMesh Query is provided in generated code. That can be easily customized further. Tip: Get code block to fetch REST endpoint from respective auto-generated REST controller.

See Code Example

// -------------------- DataMesh ------------------------- 
@QueryMapping
@PreAuthorize("hasRole('USER')")
public List<DgproductTblRec> DgproductTblRecDataMesh()
throws Exception
{
// Fetch From A. GraphQL API query resolver
List<DgproductTblRec> DgproductTblRecList = new ArrayList<DgproductTblRec>();
try {

DgproductTblRec1Repository.findAll().forEach(DgproductTblRecList::add);

System.out.println("Data Mesh Source #1 Record Count: "+DgproductTblRecList.size());

// Fetch From B. GraphQL API endpoint
// ---- Combine Data With ----------------------------------------------------
// Get data from GraphQL API call (sample shows getting data from same table ViewAll API call)
//----------------------------------------------------------------------------
String get_data_url = "http://127.0.0.1:9070/graphql";
WebClient webClient1 = WebClient.builder().baseUrl(get_data_url)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build();

HttpGraphQlClient graphQlClient = HttpGraphQlClient.builder(webClient1)
//.headers(headers -> headers.setBasicAuth("UserId", "..."))
.build();

String gql_doc =
" query { "+"\n"+
" DgproductTblRecViewAll { "+"\n"+
" productId , \n\t productType , \n\t productDesc , \n\t productCategory , \n\t primarySupplier , \n\t productPicture , \n\t "+"\n"+
" } "+"\n"+
"} "+"\n";
Mono<List<DgproductTblRec>> response =
graphQlClient.document(gql_doc)
.retrieve("DgproductTblRecViewAll")
//.toEntity(DgproductTblRec.class);
.toEntity(new ParameterizedTypeReference<List<DgproductTblRec>>() {});

List<DgproductTblRec> getMeshGqlListDgproductTblRec = response.block();

System.out.println("Data Mesh Source #2 Record Count: "+getMeshGqlListDgproductTblRec.size());

getMeshGqlListDgproductTblRec.forEach(DgproductTblRecList::add);
//----------------------------------------------------------------------------

// Fetch From C. REST API endpoint
// get from respective REST controller
// ---- Combine Data With ----------------------------------------------------
// Get data from REST API call (sample shows getting data from same table ViewAll API call)
//----------------------------------------------------------------------------
String get_data_rest_url = "http://127.0.0.1:9080/emdbrest/dg_product/ViewAll?pageNo=-1"; //-1 == Unpaginated
WebClient webClientRest = WebClient.builder().baseUrl(get_data_rest_url)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build();

Mono<List<DgproductTblRec>> responseR =
webClientRest.get()
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).retrieve()
.bodyToMono(new ParameterizedTypeReference<List<DgproductTblRec>>() {});

List<DgproductTblRec> getMeshListDgproductTblRec = responseR.block();

System.out.println("Data Mesh Source #2 Record Count: "+getMeshListDgproductTblRec.size());

getMeshListDgproductTblRec.forEach(DgproductTblRecList::add);
//----------------------------------------------------------------------------


} catch (Exception e) {
System.out.println("Error: Exception: "+e.getMessage());
//e.printStackTrace(System.out);
throw new Exception(e.getMessage());
}
return DgproductTblRecList;
}