NexJ Logo

Enabling and configuring REST API v2

Enabling REST API v2

To enable REST API v2, you need to add a mixin model to the project and update the environment file.

Including the restapi mixin model

Include the "RestV2 API Infrastructure" (restapi) model in your project. Do one of the following:

  • Add the  <Mixin namespace="nexj:model:restapi" version="1+"/> parameter to the  <Mixins> tag in your environment file.
  • In NexJ Studio, use Edit Mixin Models  dialog  to add the model to the project.

Updating environment attributes

The following attributes are related to the REST API. 

NameRequired?DescriptionExample value
rest.versionRequiredThe current version of the REST API.1.0.0
rest.schemaBaseRequiredThe base message for available REST resource mappings. For more information, see  Creating a schema base mapping message.CRMSchemaBaseJSON
rest.defaultPageSizeOptionalDefault page size for GET requests.200
rest.configMetadataRequired

Metaclass with REST API configurations such as resource names.

CRMRestAPIConfig

Add this code to the Environment root element in your project environment file:

<Environment ... rest.configMetadata="CRMRestAPIConfig" rest.schemaBase="CRMSchemaBaseJSON" rest.version="1.0.0"... />

Updating environment channel connections

Three HTTP channel connections are required for using the REST API:

  • OpenAPI
  • REST
  • SelectionOptions

To add these connections, include the following code in the environment file in your project.

Channel connections in the environment file
   <ChannelConnections>
      <HTTPConnection channel="rest:OpenAPI"/>
      <HTTPConnection channel="rest:REST"/>
      <HTTPConnection channel="rest:SelectionOptions"/>
   </ChannelConnections>

Channel definitions in the restapi model are initialized with basic authentication. Project-specific authentication components should be specified in the channel connection settings in the project environment files.

Enabling single sign-on authentication

If your project already has single sign-on authentication configured then ignore this section.

REST API v2 requires the SSO dependency model for accessing user context in integration services. The dependency model itself is included in the application project, but you must configure the data source in your project environment.

Add the following code to the RelationalDatabaseConnection element in the environment file:  <DataSource name="sso:SSO"/>

Add the PKIKeyPair element below to the environment file in your project.

SSO PKIKeyPair
   <PKIKeyPairs>
      <PKIKeyPair keystore="<keystore>" name="nexjsa" password="text:<password>"/>
   </PKIKeyPairs>


For more information about PKI keys and enabling single sign-on authentication, see Configuring single sign-on and Adding PKI keys to an environment file.

Configuring REST API v2

The following diagram demonstrates how the configurations are connected to provide the APIs. In this example, the Entity class requires an API specification.

Before you start, configure the environment file to initialize the environment attributes and channel connections and perform any necessary configurations to enable SSO.

  1. Create the RestAPIConfig configuration class. This class is derived from the rest:BaseRestAPIConfig class defined in the "RestV2 API Infrastructure" (restapi) model. It contains the resource mappings for the Entity class.
  2. Create the EntityJSON1.0.0 message to define the API specification for the Entity class.
  3. Create the SchemaBaseJSON message as the schema base for the project. The schema base message is used by "RestV2 API Infrastructure" (restapi) model to provide API support for resources. The message references the EntityJSON1.0.0 message object.  
  4. Update the configMetadata and schemaBase attributes in the project environment file.


Creating a configuration class

Projects implementing REST APIs must create their own metadata class containing API configurations. This must derive from the rest:BaseRestAPIConfig class, which provides the template for available configurations.

The name of the configuration class must be specified in the project environment file using the rest.configMetadata environment variable.

The following configurations are available from the rest:BaseRestAPIConfig template:

AttributeDescriptionConfigurationDefault value
mapResourceNamesCollection of available resources.

By default, this attribute points to the getResourceNameMap event. This event is used to configure the collection of resource messages.

Must be overridden.
INFO_SWAGGER_UIMessage defining info block for SwaggerUI.Allows configuration of the information header block for SwaggerUI. Configures title, description, and version.(message
      (: version (rest:ObjectSnapshotManager'CURRENT_VERSION))
      (: title "NexJ CRM")
      (: description "RESTful API for managing NexJ CRM data")
)

The following example shows the inform:RestAPIConfig configuration class for the inform model.

--- inform:RestAPIConfig.meta ---
<Class base="rest:BaseRestAPIConfig">
	   <Attributes>
      		<Attribute description="Message defining info block for SwaggerUI." name="INFO_SWAGGER_UI" static="true" type="any" value="
	(message
   		(: version "1.0.0")
   		(: title "NexJ Inform")
   		(: description "RESTful API for NexJ Inform functionality")
	)"/>
   </Attributes>
   <Events>
      <Event name="getResourceNameMap" static="true">
         <Actions>
            <Action name="main" type="main"><![CDATA[(collection
   (message
      (: resourceSingular "interest")
      (: resourcePlural "interests")
      (: className "inform:Interest")
   )
)]]></Action>
         </Actions>
      </Event>
   </Events>
</Class>

You can now create API endpoints for resources will be exposed through REST API, map these resources to metadata classes, and configure classes for REST API v2 features. 

Defining resource mapping event

Resource mappings are defined in the getResourceNameMap event in the metadata configuration class for the project.

Here is an example of a resource mapping for an Interest class, defined in inform:RestAPIConfig.meta:

Example: Resource mapping
<Class base="rest:BaseRestAPIConfig">
   <Events>
      <Event name="getResourceNameMap" static="true">
         <Actions>
            <Action name="main" type="main"><![CDATA[(collection
   (message
      (: resourceSingular "interest")
      (: resourcePlural "interests")
      (: className "inform:Interest")
   )
)]]></Action>
         </Actions>
      </Event>
   </Events>
</Class>

Available properties for the resource mappings are the following:

NameRequired?

Description

resourcePluralRequired

API endpoint for accessing resource. In the example, the resource can be accessed using:

<URL for REST Channel>/interests

resourceSingularRequiredSingular API endpoint used for accessing OpenAPI specification for resource.
classNameRequiredClass name associated with the resource. In the example, the resource maps to the inform:Interest class.
htmlPathOptional

HTML path for accessing an instance in the UI. This is relevant for providing UI links in a request.

introductionVersionOptionalThe API version that introduces this resource. This is relevant for OpenAPI documentation purposes.
deprecationVersionOptionalThe API version that deprecates this resource. This is relevant for OpenAPI documentation purposes.

Creating JSON resource messages

The JSON resource message for a class defines the class attributes that are accessible using REST APIs.

JSON resource messages must follow the following naming convention: {ClassName}{Format}.{Version}. This naming convention is required to construct responses with correct class, for the correct version, and with the correct format. The type for all values must be set to string.

For example, a resource message for the Interest class is called InterestJSON.1.0.0.

Example: JSON resource message
<Message format="JSON">
   <Parts>
      <Value description="Unique ID of the object." name="id" type="string"/>
      <Value description="Related object associated with this instance." name="relatedObjectId" type="string"/>
      <Value description="Value used to retrieve list of associated articles." name="value" type="string"/>
   </Parts>
</Message>

Creating a schema base mapping message

In addition to creating resources messages, you must define message parts in the relevant schema base message.

The schema base mappings are a snapshot of the available resources using API. It is required so that projects can have independent API resources without having to customize schema for base projects. For example, a client project can have its own schema base, which is different from the base project and is maintained independently. This is also useful for testing. Projects can have a separate schema for testing, by setting the schema base at runtime in a unit test, which are not exposed publicly.

The schema base message has to follow the naming convention {rest.schemaBase}{format}.{rest.version}. The schema base message is defined in the rest.schemaBase environment attribute. 

Each resource has a detail definition and a summary definition in the schema base message.

  • Detail objects reference the resource message definition for a class. They have access to all attributes that are exposed in the resource message.
  • Summary objects are a minimal version of the message definition that is accessible for read queries. To optimize read queries, collection associations that require joins (for example, participants) are not usually included in a summary object.

The example below is taken from the ObjectSnapshotJSON.1.0.0 schema base message.

<Message description="Message structure for Snapshot manager. This is intended for defining RESTv2 schema. This message metadata does not rely on NexJ framework for parsing or formatting" format="JSON">
   <Parts>
      <Message description="Detail reads" name="data">
         <Parts>
            <Message maxCount="0" name="Interest" ref="InformJSON.1.0.0"/>
			...
         </Parts>
      </Message>
      <Message description="Message schemas for collection GET queries. Intended to support complex export behaviors with calculated attributes, without impacting Create/Update behaviors" name="summary">
         <Parts>
            <Message maxCount="0" name="Inform">
               <Parts>
                  <Value description="Unique ID of the object." name="id" type="string"/>
                  <Value description="Related object associated with this instance." name="relatedObjectId" type="string"/>
                  <Value description="Value used to retrieve list of associated articles." name="value" type="string"/>
               </Parts>
            </Message>
           	...
         </Parts>
      </Message>
   </Parts>
</Message>

Configuring metadata classes to use REST API v2

You need to configure metadata classes to enable them to use various REST API v2 features.

The following class properties must be set:

  • nameAttribute
  • caption

They are used for informative error handling and constructing user-friendly responses.

Configuring class attributes

For constructing user-friendly responses conforming to OpenAPI standards, you can configure class attributes using the facets defined in the rest:SubType.facets file. Depending on the attribute type, the configuration may be required.

The following facets are available for the timestamp attribute type:

FacetDescriptionFormatting
Example
UTC_DATETIME

A timezone-agnostic timestamp attribute. For example, activity start time.

2020-03-08T04:00:00Z
PURE_DATE

A timezone-agnostic date attribute. For example, date of birth.

2020-03-08
DATETIME_WITH_TIMEZONEA timezone-sensitive timestamp attribute. For example, a service request resolution time used for reporting.2020-03-08T21:00:00Z
DATE_WITH_TIMEZONEA timezone-sensitive date attribute. For example, coverage start date for an entity.2020-03-08
When you add any timestamp attribute which includes timezones, you also need to add a string attribute named "unit_<attributeName>". 

This is an example of a class configuration with SubType facets for timestamp attributes.

Example: Timestamp attributes with SubType facets
<Class caption="rest.TestObjectSnapshot.caption" nameAttribute="name">
   <Attributes>
      <Attribute description="Name attribute for class." name="name" type="string"/>
      <Attribute description="Attribute with "DATETIME_WITH_TIMEZONE" SubType facet." facets="(SubType (DATETIME_WITH_TIMEZONE))" name="datetimeWithTimeZone" type="timestamp"/>
      <Attribute description="Attribute with "DATE_WITH_TIMEZONE" SubType facet." facets="(SubType (DATE_WITH_TIMEZONE))" name="dateWithTimeZone" type="timestamp"/>
      <Attribute description="Attribute with "PURE_DATE" SubType facet." facets="(SubType (PURE_DATE))" name="pureDate" type="timestamp"/>
      <Attribute description="Attribute with "UTC_DATETIME" SubType facet." facets="(SubType (UTC_DATETIME))" name="datetimeUTC" type="timestamp"/>
      <Attribute description="Unit for dateTimeWithTimeZone attribute." name="unit_datetimeWithTimeZone" type="string" value=""UTC""/>
      <Attribute description="Unit for dateWithTimeZone attribute." name="unit_dateWithTimeZone" type="string" value=""UTC""/>
		...
   </Attributes>
   ...
</Class>

Configuring the ALTERNATE_KEY aspect

The ALTERNATE_KEY aspect can be implemented by a class to enable lookup using an alternate unique attribute instead of OID. This is used for classes such as User, which have unique user names.

Instances of classes implementing this aspect inherit uniqueness validation by the alternate key for create and update commands.

The following inherited attributes are configurable for this aspect.

AttributeRequired?DescriptionExample
API_KEYRequired

Association path to the alternate key to use in place of OID.

'(@ userName)
API_KEY_ATTRIBUTE_LISTOptional

Attribute list to automatically include the alternate key in attribute load.

'(firstName lastName)
API_KEY_DESCRIPTIONOptional

Description of API_KEY value for use in the automatically generated documentation.

The value should be a string reference to support localization.

rest.TestObjectTemplate.keyDescription
API_KEY_EXAMPLEOptional

Example of API_KEY value for use in the automatically generated documentation.

wm1

This is an example configuration of the ALTERNATE_KEY aspect in a TestObjectSnapshotTemplate class:

Example: ALTERNATE_KEY configuration
<Class aspects="rest:ALTERNATE_KEY" caption="rest.TestObjectSnapshotTemplate.caption" nameAttribute="title">
   <Attributes>
      <Attribute description="Name attribute of class." name="title" type="string"/>
      <Attribute description="Attribute used as alternate key." name="API_KEY" static="true" type="any" value="'(@ title)"/>
      <Attribute description="Description of alternate key." name="API_KEY_DESCRIPTION" static="true" type="string" value=""api.TestObjectSnapshotTemplate.keyDescription""/>
		...
   </Attributes>
	...
</Class>

This is an example of a GET request and response for a TestObjectSnapshotTemplate instance with title "TestTemplate1":

Example: Request and response using ALTERNATE_KEY
Request:
<Base URL>/channel/<API Channel>/testObjectSnapshotTemplates/TestTemplate1

Response:
{
   "$commit-hash": 256141559,
   "title": "TestTemplate1"
}

Configuring the TEMPLATE aspect

The TEMPLATE aspect is used to simplify assignment of template classes when using the selection options API. For example, when requesting selection options for an activity template, all properties of the template may not be important and only  the value and caption may be required for the purpose. This is what the TEMPLATE aspect provides.

The following inherited attributes are configurable from this aspect:

AttributeRequired?Description
valueRequired

Reference the string attribute to use as value. Classes implementing this aspect should have this attribute within that class, or overridden from the aspect. If the attribute is not persisted, a processWhere function is required to enable query by "value" for template assignments.

ordinalOptional

Ordinal attribute for sorting.

Defaults to ordinal of Class'read if unspecified.

TEMPLATE aspect configuration example

In this example, the TestObjectSnapshot class has an association to the TestObjectSnapshotTemplate class. 

With this configuration, a response with TestObjectSnapshotTemplate instances is returned when TestObjectSnapshot'template is requested using selection options. Note that the response messages only contains caption and value properties provided by the TEMPLATE aspect.

This is an example configuration of TEMPLATE aspect in a TestObjectSnapshotTemplate class:

Example: TEMPLATE configuration
--- TestObjectSnapshot.meta ---
<Class caption="rest.TestObjectSnapshot.caption" nameAttribute="name">
    <Attribute description="Association implementing rest:Template aspect." name="template" type="rest:TestObjectSnapshotTemplate"/>
	...
</Class>

--- TestObjectSnapshotTemplate.meta ---
<Class aspects="rest:TEMPLATE" caption="rest.TestObjectSnapshotTemplate.caption" nameAttribute="title">
   <Attributes>
      <Attribute description="Value used for template." name="value" type="string" value="(@ title)"/>
      <Attribute description="Name attribute of class." name="title" type="string"/>
		...
   </Attributes>
	...
</Class>

This is an example of a request using selection options to get the templates for a TestObjectSnapshot class:

Example: Request and response for TEMPLATE instances
Request:
<Base URL>/channel/<Selection options Channel>/testObjectSnapshots/template

Response:
{
   "options": [
      {
         "sortOrder": 0,
         "value": "TestTemplate1",
      },
      {
         "sortOrder": 1,
         "value": "TestTemplate2",
      }
   ]
}

Configuring APIs for mixins and parent projects

REST API v2 infrastructure does not automatically publish APIs from mixins or parent projects. If you want to expose APIs for models imported from a mixin or parent project, you must define them explicitly as part of the API specification. This prevents projects from exposing unintended APIs externally.

In order to expose an API for an imported model that already has an API specification message defined, you must do the following:

  • Add a resource mapping to the configuration class.
  • Add an entry in the schema base message for the project, referencing the specification message for the model.

In the example below, the inform:Interest class is imported from the inform mixin model.

  • The first section updates the getMapResourceNames event of the ExampleRestAPIConfig configuration class to include "interests" as a resource mapping.
  • The second section updates the ExampleSchemaBaseJSON schema base message for the project to include references to the inform:schema:InterestJSON.1.0.0 message, where the inform:schema:InterestJSON.1.0.0 message is imported from the inform model.
----- ExampleRestAPIConfig.meta --------
<Class base="rest:BaseRestAPIConfig">
   <Events>
      <Event description="Returns a collection of resource mappings for the finance project." name="getMapResourceNames" static="true">
         <Actions>
            <Action name="main" type="main"><![CDATA[(collection
	...
   (message
      (: resourceSingular "interest")
      (: resourcePlural "interests")
      (: className "inform:Interest")
   )
)]]></Action>
         </Actions>
      </Event>
   </Events>
</Class>

----- ExampleSchemaBaseJSON.message ------
<Message description="Message structure for classes supported by RestV2 resources" format="JSON">
   <Parts>
      <Message description="Detail reads" name="data">
         <Parts>
            ...
            <Message maxCount="0" name="inform:Interest" ref="inform:schema:InterestJSON.1.0.0"/>
         </Parts>
      </Message>
      <Message description="Message schemas for collection GET queries. Intended to support complex export behaviors with calculated attributes, without impacting Create/Update behaviors" name="summary">
         <Parts>
 			...
            <Message maxCount="0" name="inform:Interest" ref="inform:schema:InterestJSON.1.0.0"/>
         </Parts>
      </Message>
   </Parts>
</Message>

The table below illustrates example REST API links.

DescriptionLink

REST API link to the "entities" resource.

This can be used for GET, POST, PUT, and DELETE methods with
appropriate request parameters

Request all entities:
http://localhost:7080/nexj/channel/rest:REST/entities

Request a specific entity:
http://localhost:7080/nexj/channel/rest:REST/entities/<id>

Request entities with query parameter:
http://localhost:7080/nexj/channel/rest:REST/entities?lastName=Lamont&firstName=Tim

Link to the full OpenAPI spec in JSON format.http://localhost:7080/nexj/channel/rest:OpenAPI/en/openAPI.json
Link to SwaggerUIhttp://localhost:7080/nexj/rest:SwaggerUI.html

Link to selection options for entity "tier".

This assumes the tier class is configured
with the TEMPLATE aspect, and the tier association
is exposed in the API for entity.

http://localhost:7080/nexj/channel/rest:SelectionOptions/entities/tier