Technical Overview
Introduction
The API Factory is a tool that enables the definition of a REST API directly within an ABAP Class, rather than manually defining it in the API Designer.
In fact, virtually every aspect of an ABAP Class can be displayed in the Swagger UI.
Thus, the API is represented as a distinct ABAP class which implements the /NEPTUNE/IF_DYNAMIC_RESTAPI interface (metadata class), the /NEPTUNE/IF_DR_RUNTIME interface (runtime class), or both in combination.
The endpoints of the API are manifested as Interface methods of this class.
Clarification about the naming
The API Factory Framework is founded upon the ABAP Class /NEPTUNE/CL_DYNAMIC_RESTAPI and a corresponding Interface, /NEPTUNE/IF_DYNAMIC_RESTAPI.
Therefore, within the ABAP Development artifacts that we provide, you will exclusively encounter the naming pattern of DYNAMIC_RESTAPI or DR - as opposed to "API_FACTORY".
Essentially, "Dynamic Restapi" denotes the technical backend term for the development objects, while "API Factory" represents the functional name for this framework.
How exactely is the API defined?
The API Factory Class is designed to furnish the API Factory Framework with all relevant information pertaining to the REST API, such as: which endpoints it provides and what are their HTTP operations (GET, PUT, POST, DELETE)?
Subsequently, this information is exhibited in the API Factory Cockpit application and precisely documented in the Swagger UI region of the particular API Factory Class.
It is worth noting that the API Factory Framework solicits this information by means of specific Interface methods of /NEPTUNE/IF_DYNAMIC_RESTAPI, such as /NEPTUNE/IF_DYNAMIC_RESTAPI~GET_PATHS, which returns all the endpoints of the API and the configuration for their request and response bodies.
How cool would it be if you could simply execute any executable ABAP Program in your system and supply the correct selection screen parameters and select-options without the need to program this individually for each and every program and also get a nice Swagger Specification on top for this specific program?
The API Factory makes this possible and we ship you that tool with class /NEPTUNE/CL_DR_LIB_SRC_PROG_X.
Entity References
An API built with the API Factory can be parameterized as a whole, which means that all endpoints and the Swagger specification are tailored towards those parameters. These general API parameters are referred to as "Entity References." The metadata class can define and validate these references using the following methods:
-
/NEPTUNE/IF_DYNAMIC_RESTAPI~GET_ENTITY_KEY_INFO
-
/NEPTUNE/IF_DYNAMIC_RESTAPI~VALIDATE_ENTITY_KEY_INFO
Example from /NEPTUNE/CL_DR_LIB_SRC_PROG_X:
When you define Entity References using the /NEPTUNE/IF_DYNAMIC_RESTAPI~GET_ENTITY_KEY_INFO method (usually by subclassing /NEPTUNE/CL_DYNAMIC_RESTAPI and redefining the method), they will appear in the API Factory Cockpit Application. Furthermore, you can create Entity Sets, which enable you to regulate access to a group of Entities using our platform.
Architecture
The API Factory Framework consists of two primary areas that define an API: the technical and functional perspectives. The technical perspective, represented by /NEPTUNE/IF_DYNAMIC_RESTAPI, deals with the metadata of the API. Meanwhile, the functional perspective, represented by /NEPTUNE/IF_DR_RUNTIME, handles the actual execution of the API’s business logic.
When an HTTP request is made to the API Factory Framework, it first handles the technical information, then delegates the action to the runtime. After the action has been performed, the technical area again comes into play to perform post-processing tasks.
Some aspects of the technical area include determining if the request should be allowed with an ICF Subnode, deserializing the JSON request payload, determining the interface method of the functional area to execute, returning the Swagger Specification of the API if called with the suffix path …/swagger.json, and determining the Swagger Specification.
After the runtime execution, the framework serializes the result into a JSON response payload and handles any exceptions that may have occurred by serializing them into useful information with an HTTP 500 response.
Implementation possibilities
As the Rest API is comprised of two distinct aspects, each represented by an interface, it is possible to separate these into two separate classes or combine them into a single class, providing developers with flexibility in their implementation approach.
The metadata class will always need to return the actual runtime Object (itself or another object) with method /NEPTUNE/IF_DYNAMIC_RESTAPI~GET_RUNTIME |
Benefits of splitting the technical and functional area into two classes
Separating the functional and technical aspects of a REST API into two classes can offer several benefits. For example, it allows for a slimmer class that solely focuses on the business logic, making it easier to reuse in the ABAP environment. The business logic shouldn’t have to concern itself with REST API topics such as JSON serialization and deserialization. By separating these concerns, it is also possible to use the business logic internally within other ABAP resources and processes without having to worry about the technical details of the REST API.
Having a class that focuses solely on the business logic not only separates the concerns but also enables easier reuse of the class in the ABAP environment. The business logic should not be concerned with REST API-related tasks, such as JSON serialization and deserialization.
By creating a slim class that only handles the business logic, you can use it not only as a REST API but also internally within other ABAP resources and processes. This promotes code reusability and maintainability.
To illustrate this concept, let me provide a concrete example:
Let’s assume that you have created a runtime class using our wizard as described in the following documentation:
As an example, if you follow the instructions for creating a new Sales Order API using the wizard and the class ZCL_DR_SALESORDER_RUNTIME, you can easily test it using the API Factory.
This is two ways how to envoke this POST /newSalesorder with plain ABAP Coding.
One way would be (if you are only "interested" into invoking the POST requests):
Another way would be (if you are interested to invoke any endpoint):
Perhaps you can see the similarities between invoking the REST API and ABAP. It is remarkable to be able to access the same business logic from both ABAP and a REST API with identical naming conventions and ABAP class. This concept demonstrates that the business logic is independent of the technology used to execute it.
The ABAP and web developers can invoke this business logic with the same approach, even with identical parameter names, typing information, and exception handling. The ABAP TRY…CATCH
block is comparable to the web HTTP status code 500.
In the ABAP world, developers rely on the Data Dictionary (DDIC) and syntax checks to guide them on how to execute specific actions. Similarly, in the web world, the OpenAPI 2.0/3.0 Specification (also known as Swagger) serves as a guide for executing specific actions. One could even liken the OpenAPI Specification to a "web version" of the Data Dictionary (DDIC). |
Request Structure
In order for a HTTP Request to be regarded as an API Factory request and hence invoke the API Factory Framework it must be called with a specific request structure.
The request structure for a "runtime" call of the API will be as follows:
We will explain the path tools below in detail:
icfSubnode (optional):
-
If you want to enable anonymous access to the Rest API you can create an ICF Subnode and store default credentials
-
The Metadata Class will decide if the HTTP call can be performed with the Subnode of the inbound request
apiClass:
-
The API Class name for the current request. Can be a Metadata-/ and/or Runtime-Class
-
If the class implements /NEPTUNE/IF_DYNAMIC_RESTAPI it will be regarded as the metadata class
-
If the class only implements /NEPTUNE/IF_DR_RUNTIME then it is only a "Runtime" class with default technical behavior*
-
Default behavior is reflected with class /NEPTUNE/CL_DR_GENERIC which is a metadata class and handles all "plain runtime" classes technical aspects. Default settings there are: (No ICF Subnodes allowed, camelCase JSON Encoding/Decoding, …)
entityKey (optional):
-
The Metadata Class can define so called entityKeys. These can be regarded as Parameters for the whole API.
-
Based on these Entity Keys the API might accept request body payloads tailored towards this specific Entity
-
Metadata about that specific entity can be determined and then reflected in Swagger UI
endpointPath:
-
The actual endpoint path
-
Represented via a method of the runtime class
The request structure for retrieving the Swagger specification will be as follows:
You will notice that the endpointPath is statically set as "swagger.json"
Additionally you can supply an optional parameter oas_version who accepts currently the values:
-
2.0
-
3.0
Which will then control the version of the Open API Specification (aka Swagger) which will be generated. If you don’t provide this parameter the default version will be 2.0
Example Request with Metadata Classname
The Metadata class must know its runtime class and delegate the action to it. This example is a split setup where the runtime class and the metadata class are different so ZCL_MY_SPLIT_API will return an instance of ZCL_MY_SPLIT_RT. If you wanted to only use one class you could implement /NEPTUNE/IF_DR_RUNTIME directly in ZCL_MY_SPLIT_API and then return ME inside method /NEPTUNE/IF_DYNAMIC_RESTAPI~GET_RUNTIME. |
Endpoint Paths and HTTP Operations
If the endpoint path is defined via an Interface Method of the Runtime class, how does does the API Factory Frameowkr know which HTTP Operation/Verb (GET,PUT,POST,DELETE) should be assigned to the method?
Each HTTP Operation is represened as Neptune Interface:
GET /NEPTUNE/IF_DR_OPER_GET POST /NEPTUNE/IF_DR_OPER_POST PUT /NEPTUNE/IF_DR_OPER_DELETE PATCH /NEPTUNE/IF_DR_OPER_PATCH DELETE /NEPTUNE/IF_DR_OPER_DELETE
The name of the endpoint path will be converted as a camcelCase representation of the method name. MY_METHOD will become myMethod.
What if you don’t want to convert the name to camelCase representation? How can you influnence this?
RIGHT: With a metadata class and redefining the corresponding methods of /NEPTUNE/IF_DYNAMIC_RESTAPI!
Example: Your Runtime Class should have a POST endpoint called newSalesorder
You would create an Interface like ZIF_DR_SALESORDER_POST.
This interface should implement /NEPTUNE/IF_DR_OPER_*POST*
By Implementing this you mark all Methods inside ZIF_DR_SALESORDER_POST as HTTP POST endpoints.
Additionally you should of course create a method called NEW_SALESORDER inside ZIF_DR_SALESORDER_POST and create all needed parameters.