Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
Showing results for 
Search instead for 
Did you mean: 
UPDATE 2023:

  • Added link to code on github

  • Code to create client proxy must  be replaced.

ro_client_proxy ?= /iwbep/cl_cp_client_proxy=>create_rest_remote_proxy(

mo_client_proxy_int ?= /iwbep/cl_cp_client_proxy_fact=>create_rest_remote_proxy(

This blog shows how the Client Proxy (CP) can be used to consume any public REST service. The features used in this blog will be available from ABAP Platform 2022 and the corresponding SP's in SAP Note: 2512479.

For our example, the PetStore-V3 REST API from swagger has been used. Since it is based on the Open API 3.0 standard and has no authorization headers, so the setup is easy. We will use public HTTP endpoints of the service to perform typical CRUD ( Create, Retrieve, Update, Delete ) operations. Although any public REST service can be consumed via CP, it is really helpful if it is based on Open API 3.0 because then the field mapping from JSON to ABAP is easy to infer.

Analyzing the Service

Before we can consume the service we should perform the following analytical tasks to better understand the service we are going to consume and also the corresponding CP coding:

  • Identifying Entities

  • Identifying Endpoints

In our case, we do this via postman but you can use any method of preference.
Please note that this step is entirely optional but highly recommended, if you want to skip this section go directly to "Consuming Service via CP" heading

Importing the service definition in Postman

Download the Open API 3.0 definition for the service and do the following:

        1. Go to Workspaces->My Workspace (Or any other in your Postman).

        2. Click on APIs.

        3. Click on Import button and open the JSON file downloaded above.

        4. A pop-up will appear as shown below, confirm that the information for Name and Format  is as shown here and then click the Import button.

          The API definition should now be showing in your postman.

Identifying Entities

  • Expand the newly added entry "Swagger Petstore - OpenAPI 3.0" in the navigation sub-tree on the left hand-side, then click on draft and then the Definition Tab as highlighted below.

  • Here you can see all components, paths and other information for the service. For our purpose, we are going to consume the Pet component only.

  • Take note of the JSON structure of this object and get an understanding for all fields names, types and nesting. For example: the Tag and Category components are contained within the Pet.

Pet Component as defined in the API

Identifying Endpoints

To view and call all the API HTTP endpoints, first set the environment variable correctly.

  1. Click on draft -> Swagger Petstore - OpenAPI -> Variables and set the baseURL to

  2. Navigate to Collections in left-most Navigation pane and click on the PetStore collection. All API endpoints should now be working.

For this example we will use the highlighted requests only, take a note of these as we will refer to them often throughout the tutorial. They have been listed here again for convenience:

  1. Find pet by id

  2. Update a pet in the store

  3. Update an existing Pet

  4. Add a new pet to pet store

  5. Delete a pet

Now that we have analyzed the service and decided what we want to consume, we can start our work in ABAP!

To directly run this example. visit the following URL and follow instructions. you will have a working copy of the code. For details follow on below.

GitHub - SAP-samples/abap-odata-client-proxy-samples: Sample code to show customers and partners how...

Consuming Service via CP

Following classes must be defined for consuming the service:

  1. The interface for structure types and constants

  2. The client proxy model

  3. The client to consume the service

Each of these is described in detail in the sections below. Just copy the code snippets in order and by the end you will have all three classes. All blocks of code have been explained thoroughly.
Note: when copying the code in this blog replace the suffix ZAF_ with your own one

Interface for structure types and constants

Create a new interface and give it a suitable name e.g. "zaf_if_petstore_types".

To facilitate our other classes and for cleanliness, we will group together constants and structure types in this interface. Copy the ABAP code below in your interface class.

Structure types

The PET structure with all its underlying entities and fields has to be defined as shown. Since it contains the TAG and CATEGORY entities they have to be defined before.

ABAP definition

BEGIN OF tys_category,
id TYPE i,
name TYPE string,
END OF tys_category,

BEGIN OF tys_tag,
id TYPE i,
name TYPE string,
END OF tys_tag,

BEGIN OF tys_pet,
id TYPE i,
name TYPE string,
category TYPE tys_category,
status TYPE string,
END OF tys_pet.

The definition is based on the JSON structure of the entity. Looking at the XML response of a get request, we get an idea of the structure. A more precise way is to check the Open API 3.0 schema definition via Postman

XML response of a get request (DO NOT COPY!)


Next we define resource names and paths for each endpoint that we want to consume. So for all five endpoints identified previously, we have to define a resource path/name.

For instance:
To call the API endpoint: /pet/4
the resource path /pet{id} has to be defined, where {id} is the placeholder for id parameter.
Similarly, we do this for all the other endpoints as well.

Therefore the following constants have to be defined:

  • gcs_resourcse_paths, which contains the entire path with placeholders for parameters.

  • gcs_resource_names is simply a name we use internally, you can choose whatever you want

Copy and paste this code in your interface class to define the constants
    BEGIN OF gcs_resource_names,
pets TYPE /iwbep/if_v4_rest_types=>ty_internal_name VALUE `PETS`,
pet_id TYPE /iwbep/if_v4_rest_types=>ty_internal_name VALUE `PET_ID`,
pet_id_name_status TYPE /iwbep/if_v4_rest_types=>ty_internal_name VALUE `PET_ID_NAME_STATUS`,
END OF gcs_resource_names,

BEGIN OF gcs_resource_paths,
pets TYPE string VALUE `/pet`,
pet_id TYPE string VALUE `/pet/{id}`,
pet_id_name_status TYPE string VALUE `/pet/{id}?name={name}&status={status}`,
END OF gcs_resource_paths.

Now the interface class is complete, next the Client Proxy Model has to be defined.

Defining the Client Proxy Model.

The Client Proxy Model performs these tasks:

  • Defines the structure for entities to be consumed using ABAP structures

  • Defines resources for each http endpoint.

  • Define one or more operations on each resource.

Create a new class  e.g. zaf_cl_rest_petstore_model and copy paste the code snippets in this section one-by-one, in order.

Class Definition

  • The public method /iwbep/if_v4_mp_basic_rest~define is an interface method which must be implemented by every consumption model class.

  • define_pet defines our resource to be consumed and takes as input the model object

  • All models must inherit the /iwbep/cl_v4_abs_model_prov base class.
    CLASS zaf_cl_rest_petstore_model DEFINITION
    INHERITING FROM /iwbep/cl_v4_abs_model_prov


    METHODS /iwbep/if_v4_mp_basic_rest~define REDEFINITION.


    "! <p class="shorttext synchronized" lang="en">Define the structure type and resources for Pet</p>
    "! @parameter io_model | <p class="shorttext synchronized" lang="en">Proxy model</p>
    "! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en">Gateway exception</p>
    METHODS define_pet
    io_model TYPE REF TO /iwbep/if_v4_rest_model
    /iwbep/cx_gateway .



Class Implementation

Both methods are implemented using the code in snippets below. Just copy and paste the code snippets and everything should work. Important points are explained for each snippet

Method /iwbep/if_v4_mp_basic_rest~define .

This method just calls the inner method and passes the io_model parameter.
CLASS zaf_cl_rest_petstore_model IMPLEMENTATION.

METHOD /iwbep/if_v4_mp_basic_rest~define.
define_pet( CAST /iwbep/if_v4_rest_model( io_model ) ).


Method define_pet.

The main method where we define the structure types, resources and operations that can be performed on the service.

Data Declaration

  • Each of the three entities to be consumed need an ABAP structure for structure definition.
      METHOD define_pet.
    ls_data_container_pet TYPE zaf_if_petstore_types=>tys_pet,
    ls_data_container_tag TYPE zaf_if_petstore_types=>tys_tag,
    ls_data_container_category TYPE zaf_if_petstore_types=>tys_category,
    lo_structured_type TYPE REF TO /iwbep/if_v4_rest_struc_type,
    lo_resource TYPE REF TO /iwbep/if_v4_rest_resource,
    lo_operation TYPE REF TO /iwbep/if_v4_rest_operation.


Defining Structures

  • The nested structure types Tag and Category must be defined first, followed by Pet

  • All primitive property names must be lowercase to match the JSON field names.

  • Since the Pet structure contains Tag and Category, they must be added as properties to the Pet Type

*Define Category
lo_structured_type = io_model->create_struct_type_by_struct(
iv_name = zaf_if_petstore_types=>gcs_internal_struct_type_names-category
is_structure = ls_data_container_category
iv_do_gen_prim_props = abap_true ).

lo_structured_type->camelcase_lower_prim_prp_names( ).

*Define Tag
lo_structured_type = io_model->create_struct_type_by_struct(
iv_name = zaf_if_petstore_types=>gcs_internal_struct_type_names-tag
is_structure = ls_data_container_tag
iv_do_gen_prim_props = abap_true ).

lo_structured_type->camelcase_lower_prim_prp_names( ).

*Define Pet
lo_structured_type = io_model->create_struct_type_by_struct(
iv_name = zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
is_structure = ls_data_container_pet
iv_do_gen_prim_props = abap_true
iv_do_gen_prim_prop_colls = abap_true ).

lo_structured_type->camelcase_lower_prim_prp_names( ).

lo_structured_type->create_structured_property( 'CATEGORY'
)->set_external_name( 'category'
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-category ).

lo_structured_type->create_structured_property( 'TAGS'
)->set_external_name( 'tags'
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-tag )->set_is_collection( ).


Defining Resources and Operations

  • Resources are defined for each unique http endpoint/path.

  • The import parameters for the method create_resource are filled using the corresponding constants defined in the interface.

  • For parameters inside the path template, a structure type must also be defined using set_path_params_struct_type. Example
    Given this path template: /pet/{id}
    the structured type must contain one primitive property (e.g. with the name ID), that has the external name id, in our case this is simply the pet structure type.

  • Operations are defined on a resource using an HTTP verb, and can further have a request/response body

  • The response body and the request body can also be provided a structure type depending on the type of operation being performed. Example
    GET /pet/{id} has a response body with type PET but no request body
    POST /pet has a response and request body with type PET.

  • For certain unique operations such as delete in this case, an operation may not have a  structure type for a request body and response body both.

    lo_resource = io_model->create_resource( iv_name          = zaf_if_petstore_types=>gcs_resource_names-pet_id
iv_path_template = zaf_if_petstore_types=>gcs_resource_paths-pet_id ).

lo_resource->set_path_params_struct_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
)->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-get
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).

lo_resource->set_path_params_struct_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
)->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-delete
)->create_response_body( ).

*Update Status and Name
io_model->create_resource( iv_name = zaf_if_petstore_types=>gcs_resource_names-pet_id_name_status
iv_path_template = zaf_if_petstore_types=>gcs_resource_paths-pet_id_name_status
)->set_path_params_struct_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet
)->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-post
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).

lo_resource = io_model->create_resource( iv_name = zaf_if_petstore_types=>gcs_resource_names-pets
iv_path_template = zaf_if_petstore_types=>gcs_resource_paths-pets ).

lo_operation = lo_resource->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-post ).
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).

*Update with put
lo_operation = lo_resource->create_operation( /iwbep/if_v4_rest_types=>gcs_http_method-put ).
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).
)->set_structured_type( zaf_if_petstore_types=>gcs_internal_struct_type_names-pet ).


This concludes our model class, next we will define the client which will be used to execute the model operations.

Defining the client to consume the service

Create a new class in the same package as the model and give it a suitable name e.g. zaf_cl_rest_petstore_client.

Just like in the previous section, keep copying the code snippets in order of appearance. Each snippet is accompanied by a description of main concepts.

Class Definition.

The class has overall five methods, each corresponding to an operation on the API. In the Analysis section we identified five operations that we want to perform using CP, you can check them out again for reference.

For each of these operations, we will define a method, there's also method for instantiating the client proxy object. The code snippet below defines all these methods for you. The comments also explain the purpose in detail.
CLASS zaf_cl_rest_petstore_client DEFINITION


METHODS CONSTRUCTOR raising /iwbep/cx_gateway.

"! <p class="shorttext synchronized" lang="en">Create a Pet</p>
"! @parameter is_pet | <p class="shorttext synchronized" lang="en">typed structure containing create data</p>
"! @parameter rs_pet | <p class="shorttext synchronized" lang="en">typed structure containing response data from server </p>
"! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
METHODS pet_create
is_pet TYPE zaf_if_petstore_types=>tys_pet
VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet

"! <p class="shorttext synchronized" lang="en">Delete a Pet with supplied id</p>
"! This method does not return a json response body which can be serialized in to abap structure, so the http code is returned.
"! @parameter iv_id | <p class="shorttext synchronized" lang="en"></p>
"! @parameter rv_http_status_code | <p class="shorttext synchronized" lang="en"></p>
"! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
METHODS pet_delete
iv_id TYPE i
VALUE(rv_http_status_code) TYPE i

"! <p class="shorttext synchronized" lang="en">Get a pet with supplied id</p>
"! @parameter iv_id | <p class="shorttext synchronized" lang="en">The id of the pet</p>
"! @parameter rs_pet | <p class="shorttext synchronized" lang="en">Returned pet as ABAP structure, complete with deep fields.</p>
"! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
METHODS pet_read_by_id
iv_id TYPE i
VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet

"! <p class="shorttext synchronized" lang="en">Update a pet using PUT</p>
"! @parameter is_pet | <p class="shorttext synchronized" lang="en">typed structure containing update data</p>
"! @parameter rs_pet | <p class="shorttext synchronized" lang="en">typed structure containing response data from server </p>
"! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
METHODS pet_update
is_pet TYPE zaf_if_petstore_types=>tys_pet
VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet

"! <p class="shorttext synchronized" lang="en">Update the name and status of the pet having the supplied ID</p>
"! The variables to be updated are sent as query parameters. The request type is POST
"! @parameter iv_id | <p class="shorttext synchronized" lang="en">ID of the pet that is to be updated.</p>
"! @parameter iv_name | <p class="shorttext synchronized" lang="en">new name for the pet</p>
"! @parameter iv_status | <p class="shorttext synchronized" lang="en">new status for the pet</p>
"! @parameter rs_pet | <p class="shorttext synchronized" lang="en">Updated pet </p>
"! @raising /iwbep/cx_gateway | <p class="shorttext synchronized" lang="en"></p>
METHODS pet_update_name_and_status
iv_id TYPE i
iv_name TYPE zaf_if_petstore_types=>tys_pet-name
iv_status TYPE zaf_if_petstore_types=>tys_pet-status
VALUE(rs_pet) TYPE zaf_if_petstore_types=>tys_pet


DATA: mo_client_proxy TYPE REF TO /iwbep/if_cp_client_proxy_rest,
mo_http_client TYPE REF TO if_http_client.

METHODS create_client_proxy
VALUE(ro_client_proxy) TYPE REF TO /iwbep/if_cp_client_proxy_rest


Class Implementation

The implementation of each method is very similar. Using method chaining, we add the necessary information to carry out each request and also provide the payload data if required. For some methods we also receive the response data typed as PET object. The code in these sections should be self-explanatory, in case of questions just write a comment.

First add the implementation class header, followed by all the method definitions.
CLASS zaf_cl_rest_petstore_client IMPLEMENTATION.




  METHOD constructor.
mo_client_proxy = create_client_proxy( ).


 METHOD pet_create.
mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pets
)->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-post
)->set_body_data( is_pet
)->get_body_data( IMPORTING ea_body_data = rs_pet ).




  METHOD pet_delete.
DATA ls_key TYPE zaf_if_petstore_types=>tys_pet.

rv_http_status_code = mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names pet_id
)->set_path_template_parameters( ls_key
)->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-delete
)->execute( )->get_http_status_code( ).



  METHOD pet_read_by_id.
*{id} GET
mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pet_id
)->set_path_template_parameters( VALUE zaf_if_petstore_types=>tys_pet( id = iv_id )
)->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-get
)->get_body_data( IMPORTING ea_body_data = rs_pet ).



 METHOD pet_update.
mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pets
)->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-put
)->set_body_data( is_pet
)->get_body_data( IMPORTING ea_body_data = rs_pet ).



 METHOD pet_update_name_and_status.
*{id}?name={name}&status={status} POST
mo_client_proxy->create_resource( zaf_if_petstore_types=>gcs_resource_names-pet_id_name_status
VALUE zaf_if_petstore_types=>tys_pet(
id = iv_id
name = iv_name
status = iv_status )
)->create_request( /iwbep/if_v4_rest_types=>gcs_http_method-post
)->get_body_data( IMPORTING ea_body_data = rs_pet ).



 METHOD create_client_proxy.


ls_proxy_model_key TYPE /iwbep/if_cp_registry_types=>ty_s_proxy_model_key,
lv_error_text TYPE string.

url = ''
client = mo_http_client
argument_not_found = 1
plugin_not_active = 2
internal_error = 3
pse_not_found = 4
pse_not_distrib = 5
pse_errors = 6
OTHERS = 7 ).

IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO lv_error_text.
RAISE EXCEPTION TYPE lx_rest_json_pl
iv_error_text = lv_error_text.

"Proxy model defined in ZAF_CL_REST_PETSTORE_MODEL
ls_proxy_model_key = VALUE #( repository_id = /iwbep/if_cp_registry_types=>gcs_repository_id-default
proxy_model_id = 'ZAF_REST_PETSTORE'
proxy_model_version = 0003 ).

ro_client_proxy ?= /iwbep/cl_cp_client_proxy=>create_rest_remote_proxy(
is_proxy_model_key = ls_proxy_model_key
io_http_client = mo_http_client
iv_do_fetch_csrf_token = abap_false
iv_relative_service_root = '/api/v3').


Now that we have created both the Model and Client, we have to register the client proxy and setup the SSL certificate for the remote service.


Register Client Proxy

Before we can use our proxy model, it must be registered. Perform the following steps to register it:

  1. Open the Transaction /IWBEP/CP_ADMIN

  2. Click on Create and enter the details as shown below

Proxy Model ID *You can choose any value e.g ZAF_REST_PETSTORE
Version 1
Model Provider Class *Enter name of your proxy model class
Description *Enter any description
Package $TMP

After successfully completing these steps your proxy model should appear in the list of models in the transaction.

Adding SSL certificate for the remote service

The SSL certificate for the remote service has to be added in the ABAP system before it can be accessed. Refer to this blog post on how to do that.
External API Integration in SAP using REST handlers – PART 1 | SAP Blogs


Executing the Client Class Methods

Congratulations if you have come this far! Now the last step is to use our Client and Proxy Model classes to create unit tests which actually consume the service. The unit test class has one method which performs all CRUD operations and then compares actual vs expected data for each operation.

Copy this code in the unit class:
CLASS zaf_cl_petstore_test DEFINITION DEFERRED.
class zaf_cl_rest_petstore_client definition local friends

CLASS zaf_cl_petstore_test DEFINITION


pet_operations FOR TESTING RAISING /iwbep/cx_gateway.


DATA mo_cut TYPE REF TO zaf_cl_rest_petstore_client.

METHODS: setup RAISING /iwbep/cx_gateway.


CLASS zaf_cl_petstore_test IMPLEMENTATION.

METHOD pet_operations.

ls_payload TYPE zaf_if_petstore_types=>tys_pet,
ls_response_exp TYPE zaf_if_petstore_types=>tys_pet,
ls_response_act TYPE zaf_if_petstore_types=>tys_pet,
ls_category TYPE zaf_if_petstore_types=>tys_category,
lv_http_status_code TYPE string,
lv_id TYPE i,
lx_gateway TYPE REF TO /iwbep/cx_gateway.

ls_category = VALUE #(
id = 20
name = 'Persian' ).
ls_payload = VALUE #(
id = 34
name = 'Cat'
status = 'Brand New'
category = ls_category
photo_urls = VALUE #( ( |url_1| ) ) ).

ls_response_exp = ls_payload.

ls_response_act = mo_cut->pet_create( ls_payload ).

"Then the expected pet data should be returned.
cl_abap_unit_assert=>assert_equals( exp = ls_response_exp
act = ls_response_act ).
CLEAR ls_response_act.

"When we make a get request for a pet with this id.
lv_id = 34.
ls_response_act = mo_cut->pet_read_by_id( lv_id ).

"Then the expected pet data should be returned.
cl_abap_unit_assert=>assert_equals( exp = ls_response_exp
act = ls_response_act ).
CLEAR ls_response_act.

ls_response_act = mo_cut->pet_update_name_and_status(
iv_id = 34
iv_name = 'new_name'
iv_status = 'new_status' ).

ls_response_exp-name = 'new_name'.
ls_response_exp-status = 'new_status'.

cl_abap_unit_assert=>assert_equals( exp = ls_response_exp
act = ls_response_act ).

ls_payload-name = 'Schrodingers Cat'.
ls_payload-status = 'Dead and Alive'.
ls_payload-category = VALUE #(
id = 21
name = 'Mystery Kitty' ).

ls_response_exp = ls_payload.

ls_response_act = mo_cut->pet_update( ls_payload ).

cl_abap_unit_assert=>assert_equals( exp = ls_response_exp
act = ls_response_act ).

lv_http_status_code = mo_cut->pet_delete( 4 ).

"the expected pet data should be returned.
cl_abap_unit_assert=>assert_equals( exp = 200
act = lv_http_status_code ).


METHOD setup.
mo_cut = NEW zaf_cl_rest_petstore_client( ).



Once everything is set-up you can execute the unit tests and they should be green! Now pat yourself on the back and enjoy some coffee 🙂

If you enjoyed please subscribe to my Profile and share this blog with others. Also, feel free to ask any questions, provide feedback and give suggestions.

You may also follow the RESTful ABAP Programming Tag for more content and checkout these community links:

Q/A for RAP
Similar Content