Low-code Inventory Management System in Angular 10 using Apollo Angular Client

Introduction

This tutorial demonstrates an inventory management system using Hypi’s low-code serverless back-end. To send queries to the Hypi’s server, we will use Apollo client for Angular. Is it a graphql client. This library provides many beneficial functionality grouped in layers to make the connection between a graphql back-end server and a responsive front-end. With its help, we will make queries to our back-end system to carry out inventory system transactions. This builds on my last article.

Setup

To add all the require Apollo package in your angular application, you need to use the following command in your application’s IDE terminal.

ng add Apollo-angular

After you run this command, the installation package will prompt you to enter your graphql endpoint. At this point, you will enter the uri which is https://api.hypi.app/graphql.

When you will go through your project source, you will find that the package has automatically added a file named graphql.module.ts. This file is responsible to configure your Apollo client to connect to your required Graphql API endpoint. It will contain all the network details to establish accurate connectivity with specific headers and other necessary details.

Figure 1

This module has export function createApollo. It will create Apollo client using our uri and domain information along with authorization and domain. Now we will configure our headers detail in this function using Angular HttpHeaders.

Figure 2

In the httpLink.create() function, we will add headers parameter and add new HttpHeaders object along with the attributes Authorization, hypi-domain, Content-Type and Accept. So, in the return statement of this function, the link parameter is HttpLink object of Apollo containing our customized uri and headers. The next parameter is cache. The Apollo client object will receive the data from the graphql server (Hypi) and store it in this cache.

File: graphql.module.ts

 import {NgModule} from '@angular/core';
 import {APOLLO_OPTIONS} from 'apollo-angular';
 import {ApolloClientOptions, InMemoryCache} from '@apollo/client/core';
 import {HttpLink} from 'apollo-angular/http';
 import {HttpHeaders} from '@angular/common/http';
 
 const uri = 'https://api.hypi.app/graphql'; // <-- add the URL of the GraphQL server here
 
 export function createApollo(httpLink: HttpLink): ApolloClientOptions<any> {
   return {
     link: httpLink.create({uri, headers: new HttpHeaders().set('hypi-domain', 'product-01.com')
       .set('Authorization', 'eyJhbGciOiJSUzI1NiJ9.eyJoeXBpLmluc3RhbmNlIjp7InJlYWxtIjoiYXllc2hhIiwibmFtZSI6InN0b3JlIiwicmVsZWFzZSI6ImxhdGVzdCJ9LCJoeXBpLmxvZ2luIjp0cnVlLCJoeXBpLnB1cnBvc2UiOiJsb2dpbiIsImh5cGkudXNlcm5hbWUiOiJheWVzaGE5MDA5QGdtYWlsLmNvbSIsImF1ZCI6ImF5ZXNoYSIsImlhdCI6MTYwMzY5MTk4MCwiZXhwIjoxNjA2MjgzOTgwLCJzdWIiOiIwMUVKR0VFREpUS1haMzhKMEo1RUQ5V1BNUiIsIm5iZiI6MTYwMzY5MTk4MH0.p5EilLvriZAQjdwE1CH5wznqLHwww6tPz1F0NWkQgqBIRlj1OBBL3R77VhthhkQlqQmg41qbfroDf01C6VJ0kd7Wq7L8NJA8l4uGL24UgxvGaZgTGZ2CFaqcBDd0TPwbqBxW0-JXgm_1K4Lqg9Ww3m57O1zFnKVFYbTAump9qdksolar_-jUFgaAWvJ2BtSJ-AecJze9HXIdwYLqEblYE3utt9qjnzZX3WLuZAbP0UcqitdQ8YXsq8cJaDcuAHYEvBKtGzjrvRDNAGLkCqtvtlKxIciJt3vX5Ptc9ZpcyUfqF_Pe4vOygRPVDd_ngSwlP6v85_Ufpxnz0LDpU9bbug')
       .set('Content-Type', 'application/json').set('Accept', 'application/json')}),
     cache: new InMemoryCache(),
   };
 }
 
 @NgModule({
   providers: [
     {
       provide: APOLLO_OPTIONS,
       useFactory: createApollo,
       deps: [HttpLink],
     },
   ],
 })
 export class GraphQLModule {}

Transactions

We have setup all the necessary network requirements. Next, we will move on to do the transactions on our inventory management system including fetching the current stock information, editing and updating our stock and adding new ones.

To view our stock information, we have to write find query along with all the necessary variables and fields. As we have to show all the information of each product in the stock, we will include all the fields we have in our Hypi’s back-end. Next, we need all the stock, so we will use * in our variable parameter in the query. The following will be our graphql query.

{
   find(type: Product, arcql:"*"){
     edges{
       node{
         ... on Product {
           name
           hypi {
           id
           }
           price
           total
           remaining
           comments
         }
       }
       cursor
     }
     pageInfo{
       hasPreviousPage
       hasNextPage
       startCursor
       endCursor
       pageLimit
       previousOffsets
       nextOffsets
     }
   }
 }

In our angular component where we will fetch our stock and view, we will pass our graphql query using gql function from the library graphql-tag. It is really a very convenient way to write complex queries using this function rather than writing the query manually in the typescript using JSON.stringify().

 import gql from 'graphql-tag';
 
 const ProductFetching = gql`{
   find(type: Product, arcql:"*"){
     edges{
       node{
         ... on Product {
           name
           hypi {
           id
           }
           price
           total
           remaining
           comments
         }
       }
       cursor
     }
     pageInfo{
       hasPreviousPage
       hasNextPage
       startCursor
       endCursor
       pageLimit
       previousOffsets
       nextOffsets
     }
   }
 }`;

Next, we will use the watchQuery() method of Apollo in our intended typescript function. It helps to fetch the data from the back-end.

File: data-table.component.ts


 private querySubscription: Subscription;
 constructor(private apollo: Apollo){
 }
 ngOnInit(): void {
   this.querySubscription = this.apollo.watchQuery<any>({
     query: ProductFetching
   })
     .valueChanges
     .subscribe(({ data }) => {
     console.log(data);
     });
 }

The querySubscription object is of type Subscription. It is imported from the library ‘rxjs’. We will pass the ProdductFetching graphql object in the query parameter in the method. It has valueChanges property which is in fact an Observable which will emit the result from the back-end.

Next, to add a stock item in the inventory, we will use Apollo client’s graphql mutation. The syntax is almost similar to query. The difference comes when we will write mutate instead of query. Let’s go to our project’s example.

We will parse mutation upsert query in the graphql document using the graphql-tag library.

File: add-product.component.ts

const AddProduct = gql`
 mutation Upsert($values: HypiUpsertInputUnion!){
   upsert(values: $values){
     id
   } }`; 

Using the Apollo’s mutate method, we will add our product into our inventory. Here we have a function in the angular component named as addProduct() which will use the Apollo client to do the mutation part.

addProduct(): void {
   this.apollo.mutate({
     mutation: AddProduct,
     variables: {
       values: {
         Product: [
           {
             name: this.name,
             hypi: {id: this.code},
             price: this.price,
             total: this.total,
             remaining: this.total,
             comments: this.comments
           }
         ]
       }
     }
   }).pipe(map(data => {
     console.log(data);
   })).subscribe(result => {
   });
 } 

Here, the Apollo’s mutate method has parameters mutation and variables. The mutation will contain our graphql query AddProduct and the variables will contain the product which we fetched from the front-end UI. We can analyze that how convenient and simple it is to use the Apollo to do the transactions.

The same part will be used for modifying stock details. You only need to make sure that you are providing the correct code for the product and it exists in the back-end to modify its details. 

Conclusion

In this tutorial, we have done key transactions on an inventory management system built in Angular 10. The transactions are carried out using the Apollo client. First, we installed the Apollo package in our project. Then we have done the complete network configurations to connect the Angular front-end with the Hypi’s back-end server including the headers information.

Next we have demonstrated the usage of Apollo client for fetching, adding and updating/modifying the stock details. For this purpose, we have used its different methods including graphql document gql, watchQuery and mutate.

The Apollo client proves very convenient in writing the graphql queries to query the server. It helps the developer to save effort and time in writing complex graphql queries in string format in the Angular. Instead, the developer can focus on the business scenarios and make the application technically more flexible and expendable, making the code manageable.

The code is available on Github

Inventory Management System using Angular 10 and Hypi

Introduction

In this tutorial, we will demonstrate the technical implementation of low-code back-end Hypi APIs for an inventory management system and querying the Hypi APIs which it automatically generates when we build our data model known as schema. The CRUD APIs of Hypi will provide all the transactions functionality in our system.

Hypi helps to provide convenient scalable cloud applications and web services. It is best suited for the evolving business requirements. The technology is enhancing rapidly so the businesses around the world have to be efficient and time effective to carry out enormous business transactions on daily basis.

The front-end used in this demonstration will be Angular 10 application. This system will be able to increase the stock when needed and display the current situation of the present stock and modify the existing. We will query the instance we created in our Hypi App to get our desirable results.

Setup of the back-end

First we will make our back-end ready to plug with the front-end. The low-code Hypi APIs will make it so convenient and manageable to work with the stock details. The developer has to sign up for an account or use his or her existing Hypi account to create an app.

Figure 1

In the Apps page, the developer will create a new app. Here, we have created an app Inventory holding our necessary back-end implementations for the system. Next, we will go to the editor to make a graphql type definition schema for the inventory to make the transactions to the back-end and storing the stock information. It will act as a data model for our system.

Figure 2

Here we have made an object type Product. This object will be responsible to manage the stock details of the system. After saving the schema, we will create an instance of this release named product-01.com. The interesting thing to note here is that the Hypi server will attach a special object named Hypi to this data type automatically. This behavior is same for any data type a developer develops in the Hypi server.

Figure 3

In the figure 3, we have accessed the docs tab from the API editor. Here, we can see that our newly created object Product is visible and the Hypi object is automatically attached. This object is very beneficial in terms of the members it contains. For e.g., its member id is technically very informative and it will act as a unique identifier of the specific instance of the Product. We have covered its implementation in this tutorial which will be shown later.

That’s it for the back-end configuration for our application. We can see here that how convenient the Hypi’s app is. A developer does not need to indulge himself or herself in lengthy technical coding part. The Hypi’s APIs will do this part for them. It helps the developers to remain and focus on the business domain of the application more efficiently and it is obvious that it is time saving as well.

Angular 10 application setup

The structure of our angular application contains navigation and menu, build with the help of angular material 10. With the help of routing component, we will be able to navigate through our application. The interfaces are developed using angular material 10 UI components including BrowserAnimationsModules, MatToolbarModule, MatSidenavModule, MatListModule, MatFormFieldModule, MatInputModule, MatTableModule and many more.

Figure 4

The communication from the front-end to the back-end is fulfilled using an customized injectable component RequestsService.ts. It serves as service layer for our application. Using the HttpClientModule, POST requests are sent to the Hypi.

We have made a generic POST request call using function postRequest() in this .ts file. It takes two arguments, the domain and the payload. The purpose of this generic call is that any component in the app can call it using dynamic arguments.

File: requests.srvices.ts

 import {Injectable} from '@angular/core';
 import {HttpHeaders, HttpClient} from '@angular/common/http';
 import {map} from 'rxjs/operators';
 import {Router} from "@angular/router";
 
 
 @Injectable()
 export class RequestsService {
 
   constructor(private http: HttpClient, private router: Router) {
   }
 
   postRequest(domain: string, payload: string): any {
 
     const headers = new HttpHeaders()
       .set('hypi-domain', domain)
       .set('Authorization', 'eyJhbGciOiJSUzI1NiJ9.eyJoeXBpLmluc3RhbmNlIjp7InJlYWxtIjoiYXllc2hhIiwibmFtZSI6InN0b3JlIiwicmVsZWFzZSI6ImxhdGVzdCJ9LCJoeXBpLmxvZ2luIjp0cnVlLCJoeXBpLnB1cnBvc2UiOiJsb2dpbiIsImh5cGkudXNlcm5hbWUiOiJheWVzaGE5MDA5QGdtYWlsLmNvbSIsImF1ZCI6ImF5ZXNoYSIsImlhdCI6MTYwMzY5MTk4MCwiZXhwIjoxNjA2MjgzOTgwLCJzdWIiOiIwMUVKR0VFREpUS1haMzhKMEo1RUQ5V1BNUiIsIm5iZiI6MTYwMzY5MTk4MH0.p5EilLvriZAQjdwE1CH5wznqLHwww6tPz1F0NWkQgqBIRlj1OBBL3R77VhthhkQlqQmg41qbfroDf01C6VJ0kd7Wq7L8NJA8l4uGL24UgxvGaZgTGZ2CFaqcBDd0TPwbqBxW0-JXgm_1K4Lqg9Ww3m57O1zFnKVFYbTAump9qdksolar_-jUFgaAWvJ2BtSJ-AecJze9HXIdwYLqEblYE3utt9qjnzZX3WLuZAbP0UcqitdQ8YXsq8cJaDcuAHYEvBKtGzjrvRDNAGLkCqtvtlKxIciJt3vX5Ptc9ZpcyUfqF_Pe4vOygRPVDd_ngSwlP6v85_Ufpxnz0LDpU9bbug').set('Content-Type', 'application/json')
       .set('Accept', 'application/json');
 
     this.http.post('https://api.hypi.app/graphql', payload , {headers})
       .pipe(map(data => {
         console.log(data);
       })).subscribe(result => {
     });
   }
 } 

In the postRequest() function, we have set the necessary headers information along with the instance name. in our case, it is product-01.com. The graphql API endpoint will remain single for all the transactions, i.e., it will be https://api.hypi.app/graphql. The graphql queries will be dynamic. It will be in payload variable.

Transactions of the Stock Products

To add a stock item with the necessary details, we have an add product component. It will take all the information from the end-user and will call the postRequest() function with newly entered values. We have six fields of information for each of our stock product. These are code, name, price (per piece), total (available stock), remaining (remaining of this stock) and comments.

Figure 5

To add a stock product in our app in Hypi, we need a payload consisting of two parameters. First is graphql and the second is variables to be used in the graphql. The format of graphql along with the dynamic values will be as follows

 mutation {
   upsert(
     values: {
       Product: [
         {
           hypi: { id: Code-01 }
           name: “Laptop”
           total: “100”
           price: 600.0
           comments: “Dell”
           remaining: “100”
         }
       ]
     }
   ){id}
 } 

The format of the variables will be as follows

variables: {value: {hypi: {id: “Code-01”}, name: “Laptop”, total: 100, price: 600.0, comments: “Dell”, remaining: 100}} 

Here, we can see that the Hypi’s object is being used for the code of the product. So instead of using a separate member, we used the Hypi object’s id to uniquely identify our each stock product in the back-end.

In the typescript file, we have mapped the above two parameters in the typescript. A function addProduct() defines the logic of building a payload and sending it to Hypi’s API url through the POST request using the injectable class we discussed before. The two-way binding of variables is done using [(ngModel)] in the matInput.

File: add-product.component.ts

 import { Component, OnInit } from '@angular/core';
 import {RequestsService} from '../requests.services';
 
 @Component({
   selector: 'app-add-product',
   templateUrl: './add-product.component.html',
   styleUrls: ['./add-product.component.scss']
 })
 export class AddProductComponent implements OnInit {
 
   code: string;
   name: string;
   total: string;
   remaining: string;
   comments: string;
   price: number;
   constructor(private req: RequestsService) {
 
   }
 
   ngOnInit(): void {
   }
 
   addProduct(){
     var values: string;
     var domain: string;
     
     //payload to send to Hypi
     values = JSON.stringify({
        variables: {
         value: {
           hypi: {id: this.code}, name: this.name,
           total: this.total, price: this.price, comments: this.comments, remaining: this.total
         }
       },
       query: 'mutation {upsert(values: {Product: [{hypi: {id: "'+this.code+'"} ' +
       'name: "' +this.name+'" total: '+this.total+' price: '+this.price+' ' +
       'comments: "'+this.comments+'" remaining: '+this.total+'}]}){id}} ' });
 
     //domain of our new release in Hypi
     domain = 'product-01.com';
     
     //using req object to call its postRequest()
     this.req.postRequest(domain,values);
   }
 }

On the console of our angular app, the response will be shown as the following.

 {
    "data":{
       "upsert":[
          {
             "id":"Code-01"
          }
       ]
    }
 } 

To edit an existing stock product, our payload will be mostly the same. In our graphql, we will give the Hypi id of the product along with the details we want to edit. It is that simple. No big change, no hard logic. We just have to take care of the product code. It should be accurate.

File: edit-product.component.ts

 
 
 
 <!--  /* Font Definitions */  @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:1; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:0 0 0 0 0 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-469750017 -1073732485 9 0 511 0;}  /* Style Definitions */  p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin-top:0in; margin-right:0in; margin-bottom:8.0pt; margin-left:0in; line-height:107%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri",sans-serif; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:Calibri; mso-fareast-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} pre {mso-style-noshow:yes; mso-style-priority:99; mso-style-link:"HTML Preformatted Char"; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Courier New"; mso-fareast-font-family:"Times New Roman";} span.HTMLPreformattedChar {mso-style-name:"HTML Preformatted Char"; mso-style-noshow:yes; mso-style-priority:99; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"HTML Preformatted"; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt; font-family:"Courier New"; mso-ascii-font-family:"Courier New"; mso-fareast-font-family:"Times New Roman"; mso-hansi-font-family:"Courier New"; mso-bidi-font-family:"Courier New";} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-family:"Calibri",sans-serif; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:Calibri; mso-fareast-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} .MsoPapDefault {mso-style-type:export-only; margin-bottom:8.0pt; line-height:107%;} @page WordSection1 {size:8.5in 11.0in; margin:1.0in 1.0in 1.0in 1.0in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> 
   editProduct(){
     var values: string;
     var domain: string;
     values = JSON.stringify({
       variables: {
         value: {
           hypi: {id: this.code}, name: this.name,
           total: this.total, price: this.price, comments: this.comments, remaining: this.remaining
         }
       },
       query: 'mutation {upsert(values: {Product: [{hypi: {id: "'+this.code+'"} name: "'+this.name+'" total: '+this.total+' price: '+this.price+' comments: "'+this.comments+'" remaining: '+this.remaining+'}]}){id}} ' });
 
     domain = 'product-01.com';
     this.req.postRequest(domain,values);
   }
 }

In this function editProduct(), all the structure of the payload is same. Only the difference will be in the dynamic values entered from the end-user.

Figure 6

In the figure 6, we have Remaining Quantity field as well so that the remaining stock of the product can be maintained in the back-end.

The response from the Hypi is as follows

 {
    "data":{
       "upsert":[
          {
             "id":"Code-01"
          }
       ]
    }
 } 

To view the current situation of all the stock products available in our inventory, the payload containing the graphql in parameter query will be as follows

 
 
 
 <!--  /* Font Definitions */  @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:1; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:0 0 0 0 0 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-469750017 -1073732485 9 0 511 0;}  /* Style Definitions */  p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin-top:0in; margin-right:0in; margin-bottom:8.0pt; margin-left:0in; line-height:107%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:"Calibri",sans-serif; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:Calibri; mso-fareast-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-family:"Calibri",sans-serif; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:Calibri; mso-fareast-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} .MsoPapDefault {mso-style-type:export-only; margin-bottom:8.0pt; line-height:107%;} @page WordSection1 {size:8.5in 11.0in; margin:1.0in 1.0in 1.0in 1.0in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> 
 {
   find(type: Product, arcql:"*"){
     edges{
       node{
         ... on Product {
                 hypi {
 id
 }
               name
 price
 total
 remaining
 comments
         }
       }
       cursor
     }
     pageInfo{
       hasPreviousPage
       hasNextPage
       startCursor
       endCursor
       pageLimit
       previousOffsets
       nextOffsets
     }
   }
 } 

The arcql variable is used to fetch data matching the value supplied to it. Here, the “*” wildcard means we need all the stock data available in our Hypi’s back-end app. Next, the variables parameter will contain the following piece of code

variables: {arcql: “*”}

The response from the Hypi’s API on the console will be as follows

 {
    "data":{
       "find":{
          "edges":[
             {
                "node":{
                   "hypi":{
                      "id":"Code-01"
                   },
                   "name":"Laptop",
                   "price":600.0,
                   "total":100,
                   "remaining":50,
                   "comments":"Dell"
                },
                "cursor":"Code-01"
             },
             {
                "node":{
                   "hypi":{
                      "id":"Code-03"
                   },
                   "name":"LED",
                   "price":80.0,
                   "total":100,
                   "remaining":100,
                   "comments":"HP"
                },
                "cursor":"Code-03"
             },
             {
                "node":{
                   "hypi":{
                      "id":"Code-02"
                   },
                   "name":"Handsfree",
                   "price":60.0,
                   "total":50,
                   "remaining":50,
                   "comments":"Audionic"
                },
                "cursor":"Code-02"
             }
          ],
          "pageInfo":{
             "hasPreviousPage":false,
             "hasNextPage":false,
             "startCursor":"FIRST",
             "endCursor":"LAST",
             "pageLimit":25,
             "previousOffsets":[
                
             ],
             "nextOffsets":[
                
             ]
          }
       }
    }
 } 

Talking about our angular app, we have view stock component. Its html contains mat-table angular material component’s selector because we have populated the mat-table with the stock values fetched from the back-end.

File: view-products.component.html

 <h1>Available Stock Details</h1>
 <app-data-table></app-data-table>

The logic for receiving data from the back-end is implemented in data-table.component.ts.

In our app, we have created an interface named inventory to hold our strong typed response from the Hypi we discussed above. Let us look at the implementation.

File: data-table.component.ts

Import …
import {inventory} from '../inventory';
 
domain: string;
 payload: string;
 inventoryDetail: inventory;
 
 constructor(private req: RequestsService, private http: HttpClient){
 
 }
 
 fetchData(){

  this.domain = 'product-01.com';
  this.payload = JSON.stringify({  query:'query { \n find(type: Product, arcql:"*"){ \n edges{\n node{\n... on Product {\n hypi{ \n id} \n name \n price \n total \n remaining \n comments}}cursor} \n pageInfo{hasPreviousPage \n hasNextPage \n startCursor \n endCursor \npageLimit \n previousOffsets \n nextOffsets}}}', variables: {'arcql': "*"} });
   const headers = new HttpHeaders()
     .set('hypi-domain', this.domain)
     .set('Authorization', 'eyJhbGciOiJSUzI1NiJ9.eyJoeXBpLmluc3RhbmNlIjp7InJlYWxtIjoiYXllc2hhIiwibmFtZSI6InN0b3JlIiwicmVsZWFzZSI6ImxhdGVzdCJ9LCJoeXBpLmxvZ2luIjp0cnVlLCJoeXBpLnB1cnBvc2UiOiJsb2dpbiIsImh5cGkudXNlcm5hbWUiOiJheWVzaGE5MDA5QGdtYWlsLmNvbSIsImF1ZCI6ImF5ZXNoYSIsImlhdCI6MTYwMzY5MTk4MCwiZXhwIjoxNjA2MjgzOTgwLCJzdWIiOiIwMUVKR0VFREpUS1haMzhKMEo1RUQ5V1BNUiIsIm5iZiI6MTYwMzY5MTk4MH0.p5EilLvriZAQjdwE1CH5wznqLHwww6tPz1F0NWkQgqBIRlj1OBBL3R77VhthhkQlqQmg41qbfroDf01C6VJ0kd7Wq7L8NJA8l4uGL24UgxvGaZgTGZ2CFaqcBDd0TPwbqBxW0-JXgm_1K4Lqg9Ww3m57O1zFnKVFYbTAump9qdksolar_-jUFgaAWvJ2BtSJ-AecJze9HXIdwYLqEblYE3utt9qjnzZX3WLuZAbP0UcqitdQ8YXsq8cJaDcuAHYEvBKtGzjrvRDNAGLkCqtvtlKxIciJt3vX5Ptc9ZpcyUfqF_Pe4vOygRPVDd_ngSwlP6v85_Ufpxnz0LDpU9bbug').set('Content-Type', 'application/json')
     .set('Accept', 'application/json');
 
   this.http.post<inventory>('https://api.hypi.app/graphql', this.payload , {headers})
     .pipe(map(data => {
       this.inventoryDetail = data;
       console.log(data);
 
     })).subscribe(result => {
 
   });
 
  }

In this component, we have imported the interface inventory and made its object to save the response data. The POST call will map the response data into the inventory object type. We can use the nodes array from inventoryDetail object as a datasource for our mat-table.

File: data-table.component.ts

 
 ngOnInit() {
 
   this.fetchData();
   this.dataSource = new this.inventoryDetail.find.edges;
 
 } 

On the console window, the response will look like


 {
    "data":{
       "find":{
          "edges":[
             {
                "node":{
                   "hypi":{
                      "id":"Code-01"
                   },
                   "name":"Laptop",
                   "price":600.0,
                   "total":100,
                   "remaining":50,
                   "comments":"Dell"
                },
                "cursor":"Code-01"
             },
                                 ...(more nodes)
          "pageInfo":{
             "hasPreviousPage":false,
             "hasNextPage":false,
             "startCursor":"FIRST",
             "endCursor":"LAST",
             "pageLimit":25,
             "previousOffsets":[
             ],
             "nextOffsets":[
                
             ]
          }
       }
    }
 } 
Figure 7

Conclusion

In this tutorial, we have demonstrated the detailed technical implementation of Hypi low-code back-end service in angular 10 application. The business domain used is inventory management system. We have queried the graphql APIs the Hypi provided for our data model. It automatically generated CRUD APIs when we created the data model in our Hypi app. The Hypi object, attached to our data model type Product, provides a very convenient way to keep track and make transactions to our records in the Hypi back-end.

This application is tested and scalable. The developer can use many of the Hypi APIs to add more business scenarios in this application.

Performing CRUD operations using an Angular 10 app backed by Hypi

Introduction

This tutorial provides the best simplest possible solution to help the developers to understand the technical implementation of Hypi APIs in their Angular 10 application. The topic we will discuss here is a simple user management application. The user can perform some CRUD operations in the angular application using the Hypi APIs. It uses graphql.

The important technical points will be highlighted here along with the connectivity of the Angular application to the Hypi server-less application. It will also help the developers to get the idea of some of the useful APIs it provides. The developer can enhance the functionality using the basic core concepts discussed here.

The article will start with creating an account and creating an app and its corresponding release in Hypi. Then we will connect to its instance in our angular 10 application. After the connectivity, we will perform CRUD operations on the user. 

Hypi Application Setup

Step 1: First, the developer will create an account on the Hypi website to create the server-less app. After doing so, the developer then creates an app by clicking on the button as shown in Figure 1. The user shown below has already created an app named user.

Figure 1

Step 2: In this step, the developer needs to create a release in the app. In this tutorial, we have created users-01 release. The user can create multiple releases based on the application requirements and enhancements. When the developer opens the release, the following page is opened having multiple tasks to perform. Here we will focus on the schema and the editor in which the developer uses the APIs of hypi to develop his or her app functionality.

Step 3: The schema of a release will contain customized logic of the app. The existing APIs of Hypi are already inherited in the users-01 release. In this tutorial, we will use the existing APIs provided by the Hypi. So in this case, our schema will contain no code at all.

Figure 2

Step 4: After the release is created, we will create an instance of it. This instance will be connected to our angular application. We will provide a unique domain name to our instance. In our tutorial, we have created an instance with domain name users-07-hypi.com.

Figure 3

Angular 10 application

Step 5: In our angular 10 app, we will create a user interface from where we will send the request to create and find a user. We will use the Hypi’s createAccount graphql to create a user. The angular app will send a POST request to the Hypi’s server. Let’s take a look at the technical details of connecting the angular app to the Hypi’s server.

In the angular app, we will first add the HttpClientModule in the app.module.ts along with the other imports necessary to run the app.

File: app.module.ts

import …
import …
import {HttpClientModule} from '@angular/common/http';


@NgModule({
  declarations: [
    …
  ],
  imports: [
    …,
    …,
    HttpClientModule
  ],
  providers: […],
  bootstrap: […]
})
export class AppModule { }

Step 6: Then we will make an injectable requests service, necessary to request API services from Hypi’s server.

File: requests.service.ts

import …
import {Injectable} from '@angular/core';
import {HttpResponse, HttpHeaders, HttpParams} from '@angular/common/http';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {HttpClient} from '@angular/common/http';
import {Router} from '@angular/router';
import …
@Injectable()
export class RequestsService {

  constructor(private http: HttpClient,
              private router: Router) {
  }
  postRequest() {
      var values: string;
      const headers = new HttpHeaders()
      .set('hypi-domain', 'users07-hypi.com')
      .set('Authorization', 'eyJhbGciOiJSUzI1NiJ9.eyJoeXBpLmluc3RhbmNlIjp7InJlYWxtIjoiYXllc2hhIiwibmFtZSI6InN0b3JlIiwicmVsZWFzZSI6ImxhdGVzdCJ9LCJoeXBpLmxvZ2luIjp0cnVlLCJoeXBpLnB1cnBvc2UiOiJsb2dpbiIsImh5cGkudXNlcm5hbWUiOiJheWVzaGE5MDA5QGdtYWlsLmNvbSIsImF1ZCI6ImF5ZXNoYSIsImlhdCI6MTYwMDkyMDY3NSwiZXhwIjoxNjAzNTEyNjc1LCJzdWIiOiIwMUVKR0VFREpUS1haMzhKMEo1RUQ5V1BNUiIsIm5iZiI6MTYwMDkyMDY3NX0.WxuWUK3-eCmHLmSibDQs441Yt-L6PW0Z3uQj4UOr49iLIVW0flBfAViPqqK1XZTAQCGpcbOCSazKwDmlotnyhTcoWhsbjOF9UbTtOvTJwr0WPwkKsrwLFXFMAAgyuWtaAkfhIDtWRM8OPHnY_MDQHCyfB5n-8YhtZWBkWn-WGPfXu9CCpJ-bE-fZ-tGSICmLjPCfxO4mGRC5bILc9C0C9lJ-wivA6Uo64ICLmEjn4dvX6et5OoniyYDVS9wYwCyJqQUh61oZRhY2hpPwwlQYp37D0Ek0HN4q4LJKkTL80qxvxL4XeO6kXHGO7L-W3xM_mI7IjlGHe_4jtchu2QPYXg')  .set('Content-Type', 'application/json')
      .set('Accept', 'application/json');

       ….

    });
  }
}

In the above code, we have made a postRequest function which will contain all the necessary code to make the POST request. There are two headers necessary to make a POST request to the Hypi’s server.

  1. hypi-domain: It will contain your instance domain name. In our case, it is users07-hypi.com
  2. Authorization: It is authorization token you will get from the Hypi website.
  3. Content-Type: The content type will be application/json.
  4. Accept: It will accept application/json format.

Step 7: Next, we have to make a payload to send to graphql. We will use Hypi’s Account object. The members we will discuss here will be username and password. A developer can use as many members of the Account object as needed.

The payload has mainly three parts, operation name, variables and query. The operation name will contain upsert, used to do insert or update. The variables will provide the dynamic values to be used in the graphql. In our case it is adam for username and secret12 for password. Please note that the password is a map member of the Account object in the Hypi’s API.

The payload is stored in a string variable values. To send in json form, we will use JSON.stringfy() method.

      // to create an account
      values = JSON.stringify( {operationName: 'upsert', variables: {value:
        { username: 'adam', password:
        {value: 'secret12'}} }, query: 'mutation upsert($value: AccountInput!) { createAccount(value: $value) {id}}' });

To call the API url, the following POST request will work to create the user.

      this.http.post('https://api.hypi.app/graphql', values , {headers})
      .pipe(map(data => { console.log(data); })).subscribe(result => {
}

In this request, we are calling https://api.hypi.app/graphql to execute graphql query. Next, we are passing the values variable and headers we created. The response from the API will be shown in data. We are showing it on the console using console.log().

Step 8: Next, to find a user account, we will use Hypi’s findAccount API. The following payload will do the trick, the rest of the part of POST request will remain the same.

 values = JSON.stringify( {operationName: 'findAccount', query: 'query findAccount($arcql: String!){\n find (type: Account, arcql: $arcql){\n edges{\n node{\n ... on Account {\n username\n password{value} \n emails{value}\n }} cursor} pageInfo{hasPreviousPage \n hasNextPage \n startCursor \n endCursor \n pageLimit \n previousOffsets \n nextOffsets} } }', variables: {'arcql': "username='adam'"} });

  

In the above payload, the filter arcql is used to find the data based on username. Here we will filter the data based on username Adam.

The complete POST request will be as follows.

 postRequest() {
      var values: string;
      const headers = new HttpHeaders()
      .set('hypi-domain', 'users07-hypi.com')
      .set('Authorization', 'eyJhbGciOiJSUzI1NiJ9.eyJoeXBpLmluc3RhbmNlIjp7InJlYWxtIjoiYXllc2hhIiwibmFtZSI6InN0b3JlIiwicmVsZWFzZSI6ImxhdGVzdCJ9LCJoeXBpLmxvZ2luIjp0cnVlLCJoeXBpLnB1cnBvc2UiOiJsb2dpbiIsImh5cGkudXNlcm5hbWUiOiJheWVzaGE5MDA5QGdtYWlsLmNvbSIsImF1ZCI6ImF5ZXNoYSIsImlhdCI6MTYwMDkyMDY3NSwiZXhwIjoxNjAzNTEyNjc1LCJzdWIiOiIwMUVKR0VFREpUS1haMzhKMEo1RUQ5V1BNUiIsIm5iZiI6MTYwMDkyMDY3NX0.WxuWUK3-eCmHLmSibDQs441Yt-L6PW0Z3uQj4UOr49iLIVW0flBfAViPqqK1XZTAQCGpcbOCSazKwDmlotnyhTcoWhsbjOF9UbTtOvTJwr0WPwkKsrwLFXFMAAgyuWtaAkfhIDtWRM8OPHnY_MDQHCyfB5n-8YhtZWBkWn-WGPfXu9CCpJ-bE-fZ-tGSICmLjPCfxO4mGRC5bILc9C0C9lJ-wivA6Uo64ICLmEjn4dvX6et5OoniyYDVS9wYwCyJqQUh61oZRhY2hpPwwlQYp37D0Ek0HN4q4LJKkTL80qxvxL4XeO6kXHGO7L-W3xM_mI7IjlGHe_4jtchu2QPYXg')  .set('Content-Type', 'application/json')
      .set('Accept', 'application/json');

      values = JSON.stringify( {operationName: 'findAccount', query: 'query findAccount($arcql: String!){\n find (type: Account, arcql: $arcql){\n edges{\n node{\n ... on Account {\n username\n password{value} \n emails{value}\n }} cursor} pageInfo{hasPreviousPage \n hasNextPage \n startCursor \n endCursor \n pageLimit \n previousOffsets \n nextOffsets} } }', variables: {'arcql': "username='adam'"} });

      this.http.post('https://api.hypi.app/graphql', values , {headers})
      .pipe(map(data => { console.log(data); })).subscribe(result => {

    });
  }

 

The developer can inject the request in the required component and then he or she can use this POST function and show the response in the intended UI.

File: app.component.ts

import { Component } from '@angular/core';
import {RequestsService} from './requests.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'userapp';
  constructor(private requestService: RequestsService){

  }

  findAccount() {
  	this.requestService.postRequest();

  }
}

Figure 4

Conclusion

In this tutorial, we have discussed technical details about performing some operations on user using Hypi’s APIs. The concept revolves around using Hypi’s serverless app as back-end and the angular 10 application as front-end. Using Hypi’s createAccount and findAccount graphql APIs, we have successfully created and searched a user. We have used Account object already defined in Hypi. After successful Hypi account setup and app creation, we first connected to its instance and then we have used our intended graphql APIs to create and find a user.

The source code can be found at https://github.com/hypi-universe/docs/tree/master/examples/angular-intro-01/userapp