Packaging API objects in Modules enables the option to make remote calls to these objects. This allows seamless integration with external services, while facilitating the consumption of the API objects functionality, both within the same Knowledge Base (KB) and in different KBs, with no need to access the source code.
This approach entails several benefits, such as:
- Logic reuse: by using Modules, you can leverage the logic of API Objects defined in other KBs, with no need to replicate it in your KB. This provides the possibility to reuse already developed and tested functionalities, without the effort of recreating them from scratch, or the need to know the internal details of their implementation.
- Security: before you package and publish the Module containing the API objects, you can set the Procedures as "private", by setting Object Visibility property = Private. This ensures that API users will have no access to the source code of these Procedures. That is, you hide the internal implementation of the Procedures and restrict their access to external users.
- Application modularity and scalability: by packaging an API object in a module, the logic and functionality of the object are encapsulated, and this promotes the modularity and scalability of applications. Having a packaged API object facilitates reuse, isolates implementation details, and allows for efficient application growth and adaptability.
The implementation of remote call to API Objects is done through these steps:
- API Object Definition: define the API object to be called remotely. This defines the Procedures and data structures required to implement the API Object functionality.
- Package and publish the API Object: Package the API Object in a module along with the objects it references. Be sure to mark as "private" those Procedures that do not need to be accessible from the client. Then, publish the module to make it available remotely.
- Import the Module in the client KB: in your client KB, where you are defining your application, you must import the Module you have published. This allows your application to have access to the APIs and functionalities defined in the Module.
- Configure the location of the service: once the Module is imported, you must configure the location of the service you want to access. This implies specifying the server address, port, and other details needed to establish the remote connection.
- Using the remote APIs: after configuring the location of the remote service, you can use the APIs and functionalities defined in the Module as if they were local. You can call Procedures, pass parameters and receive responses as you would with any other local object. GeneXus will handle the communication and parameter translation between the native values and those of the protocol used (e.g. REST or GRPC).
The following is an example of the use of Remote Call in API Object.
Imagine that you want to create a simple API that reads customer data and allows you to add new customers to a database.
The first step is to create a module called Customer, where you will have to define a Data Source in the form of an API Object. That is, define the necessary objects such as Transactions, SDT, etc., as seen in ListCustomers and InsertCustomer.
The structure of the Customer module must be as follows:
Once the Module is defined, you must build the model and make sure that the services are working correctly. This can be verified using third-party tools or Launchpad Tool Window.
When everything is ready, you proceed to package and publish the Module.
Note: It is advisable to include in the Module only what is necessary for the operation of the API and set at Procedure level Object Visibility property = Private for security, so that the API user does not have access to the Procedure code.
After the Module is packaged and published, you must go to the client KB, who will be responsible for consuming the service. Next, you must go to Manage Module References and import the Customer Module.
Note: If you need to modify the interface, such as the parameters or the name of the call, you can repeat the process, but having the Module already imported, the button will show "Update" and it will bring the last published version.
Having imported the Module, you can define a Procedure (or another object) and make calls to the API object. For example, to call the Insert method of APICustomers, which belongs to the Customer Module, you should do the following:
// Configuring the remote service location is necessary to indicate where you are calling the remote service.
&myLocation = new() //where myLocation is a variable Location Type
&myLocation.Host = 'localhost'
&myLocation.Port = 8082
&myLocation.BaseUrl = 'KnowledgeBaseNETSQLServer/Customer/APICustomers'
APICustomers.Location = &myLocation
APICustomers.Protocol = Protocol.Rest
&CustomerId = 4
&CustomerName = 'Mery'
&CustomerLastName = 'Smith'
APICustomers.Insert(&CustomerId, &CustomerName, &CustomerLastName, &Messages)
Msg('Information ===> ' + &Messages.ToJson(), status)
When you run this Procedure, you get the following output:
Information ===> [{"Id":"SuccessfullyAdded","Type":0,"Description":"Data has been successfully added."}]
If you want to use the gRPC protocol, you can change the setting to 'Protocol.GRPC', as follows:
APICustomers.Protocol = Protocol.GRPC
Note: Make sure to have the gRPC server up on the corresponding port.
Additionally, if the APICustomers API object includes a GAM security schema you must obtain Client ID by following the steps in HowTo: Configure the API object security scheme.
Warning: Temporary Restriction: gRPC protocol is still not supported on secure APIs.
Then you may use HttpClient data type as shown below, to obtain the Token:
//The value of client id is obtained from the backoffice of the KB server’s GAM
&AuthString = !"client_id=6K0SblhG8hFIAfuKllnjKXDaPLA8q4p8moeYfark&grant_type=password&scope=gam_user_data&username=admin&password=admin123"
&httpclient.AddHeader("Content-Type", !"application/x-www-form-urlencoded")
&httpclient.AddString(&AuthString)
&httpclient.Execute(GeneXus.HttpMethod.Post, "http://localhost:8082/KnowledgeBaseNETSQLServer/oauth/access_token")
&Oauth20AccessTokenSDT.FromJson(&httpclient.ToString())
&AccessToken = &Oauth20AccessTokenSDT.access_token
&myLocation.AuthenticationMethod=HttpAuthenticationType.OAuth
&myLocation.AccessToken = &AccessToken
The full code is:
&myLocation = new()
&myLocation.Host = 'localhost'
&myLocation.Port = 8082
&myLocation.BaseUrl = 'KnowledgeBase7NETSQLServer/Customer/APICustomers'
&AuthString = !"client_id=6K0SblhG8hFIAfuKllnjKXDaPLA8q4p8moeYfark&grant_type=password&scope=gam_user_data&username=admin&password=admin123"
&httpclient.AddHeader("Content-Type", !"application/x-www-form-urlencoded")
&httpclient.AddString(&AuthString)
&httpclient.Execute(GeneXus.HttpMethod.Post, "http://localhost:8082/KnowledgeBaseNETSQLServer/oauth/access_token")
&Oauth20AccessTokenSDT.FromJson(&httpclient.ToString())
&AccessToken = &Oauth20AccessTokenSDT.access_token
&myLocation.AuthenticationMethod=HttpAuthenticationType.OAuth
&myLocation.AccessToken = &AccessToken
APICustomers.Location = &myLocation
APICustomers.Protocol = Protocol.Rest
&CustomerId = 4
&CustomerName = 'Mery'
&CustomerLastName = 'Smith'
APICustomers.Insert(&CustomerId, &CustomerName, &CustomerLastName, &Messages)
Msg('Information ===> ' + &Messages.ToJson(), status)
This functionality is available since GeneXus 18 Upgrade 8.