Technology Blog Posts by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
harsha_reddy24
Explorer
2,828

Hi RAP Developers, Good Day

Introduction:

In many business scenarios, we need to expose BAPI-based data to SAP Fiori applications without creating database tables or CDS-based persistency. With the RESTful ABAP Programming Model (RAP), this can be achieved efficiently using Custom Entities, which allow us to call ABAP logic directly—such as BAPIs—and return the data as an OData service.

In this blog, I will walk through how to build a Custom Entity that fetches bank details by calling a BAPI, how to annotate it for UI consumption, and how to expose it seamlessly to a Fiori Elements application. This approach is useful whenever you want to reuse existing BAPIs and business logic while keeping your RAP model clean, lightweight, and high-performing.

Solution Overview:

This solution demonstrates how RAP can seamlessly integrate with classic SAP functionality by exposing BAPI output through a Custom Entity. Instead of relying on database tables or CDS views, the Custom Entity acts as a virtual data source whose data is supplied dynamically via an ABAP class.

When a Fiori Elements UI requests the entity, RAP forwards the query to the ABAP implementation class. This class reads the query parameters (filters, sorting, pagination) and uses them to call BAPI_BANK_GETDETAIL. The BAPI returns the bank’s address, master data, and related details, which are then transformed into the response structure expected by the Custom Entity.

The result is an OData-based, read-only endpoint capable of delivering real-time data from a BAPI with full RAP features such as filtering, sorting, paging, and total count support. This approach makes it easy to modernize existing SAP data retrieval logic while still leveraging stable, proven BAPIs in the backend.


Custom Entity:

  • custom entity not underlying any data source.
  • data preview is not possible, becuase this should be implemented in seperate class.
  • suitable for paging,calling bapi's and external api's.
@EndUserText.label: 'Custom Entity calling bapi'
@ObjectModel: {
    query: {
        implementedBy: 'ABAP:ZCL_BAPI_BANK'
    }
}

@UI: {
    headerInfo: {
        typeName: 'Banks',
        typeNamePlural: 'Banks',
        title: { value: 'bank_ctry' },
        description: { value: 'bank_ctry' }
    }
}

@UI: {
    presentationVariant: [{
        sortOrder: [{
            by: 'bank_ctry',
            direction: #ASC
        }],
        visualizations: [{
            type: #AS_LINEITEM
        }]
    }]
}
define root custom entity ZCE_BANK
{
      .facet                : [
      {
          id      : 'Bank_data',
          purpose : #STANDARD,
          type    : #IDENTIFICATION_REFERENCE,
          label   : 'Bank Details',
          position: 10 }
      ]
      .lineItem             : [{ position: 10 }]
      .selectionField       : [{position: 10}]
      .identification       : [{position: 10}]
  key bank_ctry                : banks;
      .lineItem             : [{ position: 20 }]
      .selectionField       : [{position: 20}]
      .identification       : [{position: 20}]
  key bank_key                 : bankk;

      .lineItem             : [{ position: 30 }]
      .identification       : [{position: 30}]
      bank_name                : banka;

      .lineItem             : [{ position: 40 }]
      .identification       : [{position: 40}]
      street                   : stras_gp;

      .lineItem             : [{ position: 50 }]
      .identification       : [{position: 50}]
      city                     : ort01_gp;

      .lineItem             : [{ position: 60 }]
      .identification       : [{position: 60}]
      BANK_NO                  : bankl;

      .lineItem             : [{ position: 70 }]
      .identification       : [{position: 70}]
      CREAT_DATE               : erdat_bf;

      .lineItem             : [{ position: 80 }]
      .identification       : [{position: 80}]
      creator                  : ernam_bf;
}


Implementation:

CLASS ZCL_BAPI_BANK DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    " RAP interface for query implementation
    INTERFACES IF_RAP_QUERY_PROVIDER.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.


CLASS ZCL_BAPI_BANK IMPLEMENTATION.

  METHOD IF_RAP_QUERY_PROVIDER~SELECT.

    "----------------------------------------------------------------------
    " Local type used to return data back to Custom Entity ZCE_BANK
    "----------------------------------------------------------------------
    TYPES: BEGIN OF TY_BANK_RESULT,
             BANK_CTRY  TYPE BANKS,
             BANK_KEY   TYPE BANKK,
             BANK_NAME  TYPE BANKA,
             STREET     TYPE STRAS_GP,
             CITY       TYPE ORT01_GP,
             BANK_NO    TYPE BANKL,
             CREAT_DATE TYPE ERDAT_BF,
             CREATOR    TYPE ERNAM_BF,
           END OF TY_BANK_RESULT.

    DATA: LT_RESULT  TYPE TABLE OF TY_BANK_RESULT,
          LS_RESULT  TYPE TY_BANK_RESULT,
          LS_ADDRESS TYPE BAPI1011_ADDRESS,
          LS_DETAIL  TYPE BAPI1011_DETAIL,
          LS_RETURN  TYPE BAPIRET2.

    " Proceed only if the consumer has requested data
    IF IO_REQUEST->IS_DATA_REQUESTED( ).

      "---------------------------------------------------------------
      " Read pagination details (TOP and SKIP)
      "---------------------------------------------------------------
      DATA(LV_TOP)  = IO_REQUEST->GET_PAGING( )->GET_PAGE_SIZE( ).
      DATA(LV_SKIP) = IO_REQUEST->GET_PAGING( )->GET_OFFSET( ).

      "---------------------------------------------------------------
      " Prepare ORDER BY clause based on sort request from UI
      "---------------------------------------------------------------
      DATA(LT_SORT) = IO_REQUEST->GET_SORT_ELEMENTS( ).
      DATA LV_ORDERBY TYPE STRING.

      IF LT_SORT IS NOT INITIAL.
        CLEAR LV_ORDERBY.

        LOOP AT LT_SORT INTO DATA(LS_SORT).
          IF SY-TABIX > 1.
            LV_ORDERBY = |{ LV_ORDERBY }, |.
          ENDIF.

          " Construct ORDER BY element (ASCENDING/DESCENDING)
          LV_ORDERBY =
            |{ LV_ORDERBY }{ LS_SORT-ELEMENT_NAME } {
               COND #( WHEN LS_SORT-DESCENDING = ABAP_TRUE
                       THEN 'DESCENDING'
                       ELSE 'ASCENDING' ) }|.
        ENDLOOP.
      ENDIF.

      "---------------------------------------------------------------
      " Derive SQL filter string from UI filter (if any)
      "---------------------------------------------------------------
      DATA(lv_conditions) = io_request->get_filter( )->get_as_sql_string( ).

      "---------------------------------------------------------------
      " Read bank master records (BNKA) based on filters
      "---------------------------------------------------------------
      SELECT BANKS AS bank_ctry,
             BANKL AS bank_key,
             BNKLZ AS bank_no,
             ERDAT AS creat_date,
             ERNAM AS creator
        FROM BNKA
        WHERE (lv_conditions)
        ORDER BY (lv_orderby)
        INTO TABLE @DATA(lt_banks)
        UP TO @LV_top ROWS
        OFFSET @LV_skip.

      "---------------------------------------------------------------
      " Loop through each BNKA record and enrich details using BAPI
      "---------------------------------------------------------------
      LOOP AT lt_banks INTO DATA(ls_bank).

        " Get detailed bank address info
        CALL FUNCTION 'BAPI_BANK_GETDETAIL'
          EXPORTING
            bankcountry  = ls_bank-bank_ctry
            bankkey      = ls_bank-bank_key
          IMPORTING
            bank_address = ls_address
            bank_detail  = ls_detail
            return       = ls_return.

        " Prepare output row
        LS_RESULT-bank_ctry   = ls_bank-bank_ctry.
        LS_RESULT-bank_key    = ls_bank-bank_key.
        LS_RESULT-bank_name   = ls_address-bank_name.
        LS_RESULT-street      = ls_address-street.
        LS_RESULT-city        = ls_address-city.
        LS_RESULT-bank_no     = ls_bank-bank_no.
        LS_RESULT-creat_date  = ls_bank-creat_date.
        LS_RESULT-creator     = ls_bank-creator.

        APPEND LS_RESULT TO LT_RESULT.

        CLEAR: ls_result, ls_address, ls_detail, ls_return.

      ENDLOOP.

      "---------------------------------------------------------------
      " Return total count for UI pagination (if requested)
      "---------------------------------------------------------------
      IF IO_REQUEST->IS_TOTAL_NUMB_OF_REC_REQUESTED( ).
        SELECT COUNT(*)
          FROM BNKA
          WHERE (lv_conditions)
          INTO @DATA(lv_count).

        IO_RESPONSE->SET_TOTAL_NUMBER_OF_RECORDS( lv_count ).
      ENDIF.

      "---------------------------------------------------------------
      " Send final result back to RAP framework
      "---------------------------------------------------------------
      IO_RESPONSE->SET_DATA( LT_RESULT ).

    ENDIF.

  ENDMETHOD.

ENDCLASS.

bank.jpg

Conclusion: 
this approach highlights how RAP can modernize SAP applications by exposing classic BAPI data through Custom Entities. It enables real-time, read-only access with full OData capabilities—filtering, sorting, paging—without changing existing backend logic, bridging the gap between proven SAP functionality and modern Fiori UIs.
Thanks For Reading, Best Wishes

3 Comments
0 Likes

Hi Harsha, Thanks for the information. How this class is linked to RAP application? can you also mention the steps for the complete process if possible.

Thanks,

Jayaram

harsha_reddy24
Explorer
0 Likes

Hi @Private_Member_73936126 
a custom entity is not based on a database table or a CDS view. To populate it with data, we must implement the corresponding class. Once the data is fetched, it is passed to our application. Since we are using BAPIs, the logic involved is entirely custom

Jelena_Perfiljeva
Active Contributor
0 Likes

One would think "surely by now every BAPI should be wrapped in a proper API by SAP", but then SAP comes up with an ABAP Cloud guide which uses exactly that as an example. Makes one question their own sanity.

@harsha_reddy24 You might want to avoid blindly copy-pasting GenAI output. These tools tend to produce bad ideas and bad code quality. I'm not going to spend time on detailed review of AI content, but even from a cursory look, the code looks "like it's 1999". You might want to use this guide next time.