NexJ Logo

Using and generating versioned APIs to invoke model services

NexJ CRM provides versioned APIs for model services that you can leverage to ensure integration compatibility across multiple NexJ releases.

You can also generate your own customized versioned APIs.

When NexJ publishes an API release, it contains API attributes that are either backwards compatible or deprecated. Each API has a name and version. For example, the format for the API name is <exampleAPI>.<versionNumber>. Deprecated API attributes still work until the release version that they belong to is retired.

To develop with a newer version of the APIs, select the next version supplied by NexJ. For example, you can replace http://localhost:7080/nexj/api/finance.1/json/Person with http://localhost:7080/nexj/api/finance.2/json/Person.

Example of differences between versioned APIs

The following example responses show how NexJ can change APIs between versions and provide different data.

Differences in responses between versioned APIs

The finance.1.api contains the initials attribute, as shown in the following example response for http://localhost:7080/nexj/api/finance.1/json/Person?$indent:

[
   {
      "$class": "Person",
      "$oid": "10031EB22558AF415592879A4E51B344E0",
      "firstName": "Kristine",
      "initials": null
   },
   {
      "$class": "Person",
      "$oid": "10C859B98830D04A96B2F7C39C0A77760E",
      "firstName": "Zineea",
      "initials": null
   },
   {
      "$class": "Person",
      "$oid": "100FF7F923BFF54FD0AF218422A5869517",
      "firstName": "Asma",
      "initials": "H"
   },
...]

For finance.2.api, the initials attribute has been retired and the lastName attribute has been added, and is now available for use, as shown in the following example response for http://localhost:7080/nexj/api/finance.2/json/Person?$indent:

[
   {
      "$class": "Person",
      "$oid": "10031EB22558AF415592879A4E51B344E0",
      "lastName": "Abrams",
      "firstName": "Kristine"
   },
   {
      "$class": "Person",
      "$oid": "10C859B98830D04A96B2F7C39C0A77760E",
      "lastName": "Ahmed",
      "firstName": "Zineea"
   },
   {
      "$class": "Person",
      "$oid": "100FF7F923BFF54FD0AF218422A5869517",
      "lastName": "Akbar",
      "firstName": "Asma"
   },
...]

Example of a versioned API

This topic provides an example of a versioned API.

Versioned API for the Person metaclass

The following code snippet shows an example of a versioned API for the Person metaclass. The example shows deprecated attributes that can be used for development but they may be retired in the near future. For example, for the Person class, affix, fullName, goesBy, householdName, lastName, and smsEmail are deprecated attributes. The firstName, homeAddress, household, and user attributes are backwards compatible and can be used for development.

<API>
   <Classes>
      ...
      <Class name="Person">
         <Attributes>
            <Attribute name="affix" type="string" deprecated="true"/>
            <Attribute name="firstName" type="string" required="true"/>
            <Attribute name="fullName" type="string" deprecated="true"/>
            <Attribute name="goesBy" type="string" deprecated="true"/>
            <Attribute name="homeAddress" type="Address"/>
            <Attribute name="household" type="Household"/>
            <Attribute name="householdName" type="string" deprecated="true"/>
            <Attribute name="lastName" type="string" required="true" deprecated="true"/>
            <Attribute name="smsEmail" type="EmailAddress" deprecated="true"/>
            <Attribute name="user" type="User"/>
         </Attributes>
         <Events>
            <Event name="firstNameSearch" static="true">
               <Arguments>
                  <Argument name="attributes" type="any"/>
                  <Argument name="where" type="any"/>
                  <Argument name="orderBy" type="any"/>
                  <Argument name="count" type="any"/>
                  <Argument name="offset" type="any"/>
                  <Argument name="xlock" type="any"/>
               </Arguments>
            </Event>
            <Event name="read" static="true">
               <Arguments>
                  <Argument name="attributes" type="any"/>
                  <Argument name="where" type="any"/>
                  <Argument name="orderBy" type="any"/>
                  <Argument name="count" type="any"/>
                  <Argument name="offset" type="any"/>
                  <Argument name="xlock" type="any"/>
               </Arguments>
            </Event>
         </Events>
      </Class>
      <Class name="Portlet">
         <Events>
            <Event name="readList" static="true">
               <Arguments>
                  <Argument name="attributes" type="any"/>
                  <Argument name="where" type="any"/>
                  <Argument name="orderBy" type="any"/>
                  <Argument name="count" type="any"/>
                  <Argument name="offset" type="any"/>
                  <Argument name="xlock" type="any"/>
               </Arguments>
            </Event>
         </Events>
      </Class>
      ...
      <Class name="UserPerson">
         <Attributes>
            <Attribute name="affix" type="string" deprecated="true"/>
            <Attribute name="firstName" type="string" required="true"/>
            <Attribute name="fullName" type="string" deprecated="true"/>
            <Attribute name="goesBy" type="string" deprecated="true"/>
            <Attribute name="homeAddress" type="Address"/>
            <Attribute name="household" type="Household"/>
            <Attribute name="householdName" type="string" deprecated="true"/>
            <Attribute name="lastName" type="string" required="true" deprecated="true"/>
            <Attribute name="smsEmail" type="EmailAddress" deprecated="true"/>
            <Attribute name="user" type="InternalUser" required="true"/>
         </Attributes>
         <Events>
            <Event name="firstNameSearch" static="true">
               <Arguments>
                  <Argument name="attributes" type="any"/>
                  <Argument name="where" type="any"/>
                  <Argument name="orderBy" type="any"/>
                  <Argument name="count" type="any"/>
                  <Argument name="offset" type="any"/>
                  <Argument name="xlock" type="any"/>
               </Arguments>
            </Event>
            <Event name="read" static="true">
               <Arguments>
                  <Argument name="attributes" type="any"/>
                  <Argument name="where" type="any"/>
                  <Argument name="orderBy" type="any"/>
                  <Argument name="count" type="any"/>
                  <Argument name="offset" type="any"/>
                  <Argument name="xlock" type="any"/>
               </Arguments>
            </Event>
         </Events>
      </Class>
   </Classes>
</API>

Consuming versioned APIs

This section describes how to consume the versioned APIs provided by NexJ.

Using Swagger UI to view versioned APIs operations

This topic shows how to use Swagger UI, which is provided by SmartBear Software, to view operations for the Person metaclass using a versioned API example.

To view all available operations for a metaclass in an easy-to-read format without having to paste the raw Swagger Interface Definition Language (IDL) into an API editor, add the following mixin to your environment file in NexJ Studio and then restart your application server.

<Mixin namespace="nexj:openapi" version="0"/>

To view operations for the Person metaclass using Swagger UI and a versioned API:

  1. Open Swagger UI by entering the following URL in your browser:
    http://<application-server>/<context-root>/openAPI.html
  2. Enter the following in the Swagger UI search field and click Explore. This example uses finance.6 as the API name.
    /nexj/api/finance.6/json?filter=Person/0
    Swagger UI opens and displays the operations you can access for the Person metaclass.
  3. [Optional] To see all of the information for contacts with the last name of Lamont, enter (= (@ lastName)“Lamont”) in the where field for the Get /Person command and click Execute.
    The following response displays all of the contacts with the last name of Lamont:
    Swagger displays all contacts with last name of Lamont
  4. [Optional] To see all of the compatible events and methods for the Person metaclass, enter the following in the search field and click Explore.
    /nexj/api/finance.6/json?filter=Person/0 &compatible=false
  5. [Optional] To see all of the attributes and values with compatibility for contacts with the last name of Lamont,
    enter (= (@ lastName) “Lamont”) in the where field for the Get /Person command and click Execute.
    The following response displays the attributes and values for contacts with the last name of Lamont:
    Reponse dislplays attibutes and values for Lamont contacts
  6. [Optional] To see information for individual contacts in the database, enter the following URL in your browser:
    http://localhost:7080/nexj/api/finance.6/json/Person
    The following code snippet shows an example of the response that displays contact information:

    [{"$class":"Person","$oid":"106EDB6271733F479CB2290E658F027844","affix":null,
    "lastName":"Abrams","smsEmail":null,"homeAddress":null,"user":null,
    "firstName":"Kristine","goesBy":null,"household":null},
    {"$class":"Person","$oid":"107A78F93BDB704FBFB7EFDAD67FB27502","affix":null,
    "lastName":"Ahmed","smsEmail":null,"homeAddress":{"$class":"Address",
    "$oid":"1067A6F5559A96405B978C8530A75B98AD"},"user":null,
    "firstName":"Zineea","goesBy":null,"household":null},
    {"$class":"Person","$oid":"107C632209808E4F2388FD1EB542CEC72B","affix":null,
    "lastName":"Akbar","smsEmail":null,"homeAddress":{"$class":"Address",
    "$oid":"105729222190A64494AEA3AF4053D113B8"},"user":null,
    "firstName":"Asma","goesBy":null,"household":null},
    {"$class":"Person","$oid":"1008CF37BCBBB744C38C702B537357A5C7","affix":null,
    "lastName":"Alpers","smsEmail":null,"homeAddress":null,"user":null,
    "firstName":"Lakisha","goesBy":null,"household":null},
    {"$class":"Person","$oid":"106C5B923EBA3E4FF4A996BBB60D966AB9","affix":null,
    ...}]
  7. [Optional] To see all the attributes and events with compatibility for the classes in finance.6.api, enter the following URL in your browser:
    http://localhost:7080/nexj/api/finance.6/json
    A response will display the attributes and events with compatibility for the classes in finance.6.api.

You have used Swagger UI to view operations for the Person metaclass using a versioned API.

Using RESTful XML calls to invoke model services

You can access RESTful XML calls using versioned APIs provided by NexJ.

To invoke versioned model services using RESTful XML calls:

  1. Send the following URL to NexJ Server using a GET request for a metaclass:
    http://<application-server>/<context-root/>api/<exampleAPI.1>/xml/<metaclassName>
  2. You will receive a response that shows an XML object containing the metaclass. For example, the following code snippet shows a response with people data for the Person metaclass.

    <_Collection><item
    xi:type="Person"><_version>0</_version><_oid>106EDB6271733F479CB2290E658F027844</_oid><_keys>affix</_keys><_keys>smsEmail</_keys>
    <_keys>homeAddress</_keys><_keys>user</_keys><_keys>goesBy</_keys><_keys>household</_keys><_values
    xi:nil="true"/><_values xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values xi:nil="true"/><_values
    xi:nil="true"/><firstName>Kristine</firstName><lastName>Abrams</lastName></item><item
    xi:type="Person"><_version>0</_version><_oid>1008CF37BCBBB744C38C702B537357A5C7</_oid><_keys>affix</_keys><_keys>smsEmail</_keys>
    <_keys>homeAddress</_keys><_keys>user</_keys><_keys>goesBy</_keys><_keys>household</_keys><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><firstName>Lakisha</firstName><lastName>Alpers</lastName></item><item
    xi:type="Person"><_version>0</_version><_oid>106C5B923EBA3E4FF4A996BBB60D966AB9</_oid><_keys>affix</_keys><_keys>smsEmail</_keys>
    <_keys>homeAddress</_keys><_keys>user</_keys><_keys>goesBy</_keys><_keys>household</_keys><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/><_values
    xi:nil="true"/>
    ...
    </_Collection

    You have accessed a versioned API through a REST endpoint.

Using SOAP to invoke versioned model services

You can use SOAP to invoke versioned model services.

Before starting this task, install SoapUI, which is provided by SmartBear Software.

To use SOAP to invoke versioned model services:

  1. Copy and paste the link to the http://<application-server>/<context-root>/<exampleAPI.1>/soap endpoint into your browser.
    When you open the link, your browser generates an XML file.
  2. Save the XML file with a WSDL extension. For example, save the file with the following name: fin2.wsdl.
  3. To display a sample request with the endpoint that you opened in your browser, open the WSDL file in SoapUI.
  4. Create your SOAP request. The following code shows an example SOAP request that asks the Person class for the specific attributes that are marked as backwards compatible or deprecated with the compatibility flag set to fullName and firstName in this case. The code also contains a count parameter, which specifies that two instances should be returned for the Person class.

    <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:x-="http://www.example.com/xml-soap"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
       <soapenv:Header/>
       <soapenv:Body soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
          <x-:invoke>
             <request xsi:type="x-:Request">
                <version xsi:type="xsd:string">2</version>
                <async xsi:type="xsd:boolean">false</async>
                <commit xsi:type="xsd:boolean">true</commit>
                <!--Optional:-->
                <invocations xsi:type="x-:Invocation-array" soapenc:arrayType="x-:Invocation[1]">
                    <Item>
                        <object>
                            <class xsi:type="xsd:string">Person</class>
                            <version xsi:type="xsd:int">2</version>
                        </object>
                        <event>read</event>
                        <arguments xsi:type="x-:anyType-array" soapenc:arrayType="x-:anyType[6]">
                                                             <Item xsi:type="x-:Expression"><text>(fullName firstName)</text></Item>
                                                             <Item xsi:type="x-:Expression"><text></text></Item>
                                                             <Item xsi:type="x-:Expression"><text></text></Item>
                                                             <Item xsi:type="xsd:int">2</Item>
                                                             <Item xsi:type="xsd:int">0</Item>
                                                             <Item xsi:type="xsd:boolean">0</Item>
                                                         </arguments>
                    </Item>
                </invocations>
             </request>
          </x-:invoke>
       </soapenv:Body>
    </soapenv:Envelope>
  5. Send the request to the server endpoint in SoapUI using an HTTP POST request.

The response returns the attributes queried in the request if they are marked as compatible in the class. The response is contained in an XML file inside a SOAP response envelope. The following code shows an example response that returns two instances of the Person class with the attributes that are marked as backwards compatible, in this case the fullName and the firstName attributes. If a user requested that this version of the API provide an attribute that is not marked as backwards compatible in the SOAP request, the user would instead receive an error in the SOAP response.

<nv:Envelope xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:nv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:nc="http://schemas.xmlsoap.org/soap/encoding/">
   <nv:Body nv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns="http://www.example.com/xml-soap">
      <ns:invoke-response>
         <response>
            <results nc:arrayType="xs:anyType[1]">
               <Item href="#a"/>
            </results>
         </response>
      </ns:invoke-response>
      <ns:Array id="a">
         <items nc:arrayType="xs:anyType[2]">
            <Item href="#b"/>
            <Item href="#c"/>
         </items>
      </ns:Array>
      <ns:TransferObject id="b">
         <class>Person</class>
         <version>0</version>
         <oid>
            <values nc:arrayType="xs:anyType[1]">
               <Item xi:type="xs:base64Binary">nx6aP5iPQlycGtYlkMfPPA==</Item>
            </values>
         </oid>
         <keys nc:arrayType="xs:string[2]">
            <Item>fullName</Item>
            <Item>firstName</Item>
         </keys>
         <values nc:arrayType="xs:anyType[2]">
            <Item xi:type="xs:string">Abrams, Kristine</Item>
            <Item xi:type="xs:string">Kristine</Item>
         </values>
      </ns:TransferObject>
      <ns:TransferObject id="c">
         <class>Person</class>
         <version>0</version>
         <oid>
            <values nc:arrayType="xs:anyType[1]">
               <Item xi:type="xs:base64Binary">YrZA3EAISJO/a0bnRzN3cg==</Item>
            </values>
         </oid>
         <keys nc:arrayType="xs:string[2]">
            <Item>fullName</Item>
            <Item>firstName</Item>
         </keys>
         <values nc:arrayType="xs:anyType[2]">
            <Item xi:type="xs:string">Agena, Michael</Item>
            <Item xi:type="xs:string">Michael</Item>
         </values>
      </ns:TransferObject>
   </nv:Body>
</nv:Envelope>

Providing versioned APIs

You may want to provide your own customized versioned APIs by generating them.

Generating versioned APIs

You can generate your own customized versioned APIs.

Before starting this task, create a meta/api folder in your project.

To generate API metadata specifying members, which are attributes and events:

  1. Navigate to the NexJ Studio Business Model layer and select the Classes tab.
  2. Double-click the required metaclass to open it. For example, open the Person metaclass.
  3. For the members that you want to be compatible, select either backward for backwards compatible or deprecated in the Compatibility drop-down as the value for the related compatibility property. For example, to change compatibility for an event, select the Events tab, select the required event, and in the Common tab, select a value in the Compatibility drop-down.
  4. Save your changes.
  5. Click the down arrow beside the Run Tool button and select Generate API.
    The Generate API dialog opens.
  6. Complete the Generate API dialog as required. Ensure you specify the following information for your API:
    • Name
    • Version
    • Output directory
  7. [Optional] You can choose to overwrite a previously generated API file by selecting Overwrite Existing File.
  8. Click Run.
  9. Move the newly generated API into the meta/api folder in your project folder.

Restart your server. You can now invoke remote procedure calls with the specified /api endpoint; for example, localhost:7080/nexj/api/enterprise.1/json/LocalUser. You will only have access to the attributes marked as compatible.