GraphQL APIs Customize
Low-Code Customize & Extend - GraphQL APIs
For corresponding details, Please refer to Resources page for locating Videos & Tutorials 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
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:
Refer to below functions to customize and complete new resolver needed:
ErpproductTblRecQuery
fromErpproductTblRecGraphqlController.java
ErpinventoryTblRecSelectWhere
fromErpinventoryTblRecGraphqlController.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;
}