OData v4 Deep Insert with ABAP RAP
Create a header and its child items in a single POST request using the RAP framework's built-in deep insert support — no manual loop required.
Overview
OData v4 deep insert lets you create a parent entity together with its associated
child entities in one atomic request. In ABAP RAP, this maps directly to the
CREATE ENTITY ... FROM ... syntax with nested structures.
The key prerequisites are:
- The child entity is declared as a composition in the CDS model
- The behavior definition exposes
createon the child association - The managed runtime handles the actual DB inserts
The ABAP Behavior Definition
First, declare the association as composition in your behavior definition
so that RAP knows to handle the child as part of the same transaction:
* Behavior definition — manage draft + deep insert
managed implementation in class ZBP_SALES_ORDER unique;
strict ( 2 );
define behavior for ZSALES_ORDER_HDR alias SalesOrder
persistent table zsales_hdr
draft table zsales_hdr_d
lock master total etag LastChangedAt
authorization master ( global ) {
field ( readonly ) SalesOrderID, CreatedAt;
create; update; delete;
draft action Activate;
draft action Discard;
draft determine action Prepare;
" deep composition — items travel with the header
association _Items { create; with draft; }
}
The CREATE Handler
The managed runtime handles deep insert automatically when the request body contains
nested items. If you need custom validation, override augmentCreate:
CLASS ZBP_SALES_ORDER DEFINITION
PUBLIC FINAL CREATE PRIVATE.
PUBLIC SECTION.
INTERFACES if_abap_behv.
ENDCLASS.
CLASS ZBP_SALES_ORDER IMPLEMENTATION.
METHOD augmentCreate.
DATA ls_order TYPE STRUCTURE FOR CREATE ZSALES_ORDER_HDR.
LOOP AT entities INTO ls_order.
" stamp server-side fields before persistence
ls_order-CreatedAt = cl_abap_context_info=>get_system_date( ).
ls_order-CreatedBy = cl_abap_context_info=>get_user_alias( ).
ls_order-%control-CreatedAt = if_abap_behv=>mk-on.
ls_order-%control-CreatedBy = if_abap_behv=>mk-on.
MODIFY ENTITIES OF ZSALES_ORDER_HDR IN LOCAL MODE
ENTITY SalesOrder UPDATE FIELDS ( CreatedAt CreatedBy )
WITH VALUE #( ( ls_order ) )
REPORTED DATA( ls_reported ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.
The OData request payload
A deep insert POST to /sap/opu/odata4/sap/zsales/srvd/sap/zsales_srv/0001/SalesOrder:
{
"SoldToParty": "CUST-0042",
"Currency": "EUR",
"_Items": [
{ "Material": "MAT-001", "Quantity": 10, "NetAmount": 250.00 },
{ "Material": "MAT-007", "Quantity": 3, "NetAmount": 99.90 }
]
}
Key Takeaways
- Declare the child association as
compositionwithcreatepermission - The managed runtime maps the nested JSON array to child entity creates automatically
- Use
augmentCreateonly for server-side field defaulting — don’t re-implement what managed gives you - Enable
with drafton the association if the parent uses draft handling