Service Bindings
RAP / CDS / ODataintermediate

Service Bindings Part 4: UI vs Web API, OData V4 vs V2

Key takeaway

Choose the right binding for each consumer: what really differs between UI and Web API bindings, when OData V2 is still justified, and how to build a separate non-draft API service on the same business object.

Explanation

📌 KEY POINTS
✅ OData V4 – UI: Fiori Elements apps with draft.

✅ OData V4 – Web API: integrations, no UI annotations needed.

✅ OData V2 only for consumers that cannot use V4.

⚠️ Draft belongs in UI services, not in APIs.

▶ WHERE WE ARE
You have a UI service for the Purchase Request (ZUI_PURCHREQ_O4). Now a second consumer appears: an external procurement system that must read requests and approve them automatically. Should it use the same service? This part answers that question.

━━━━━━━━━━━━━━━━━━━━
▶ UI BINDING VS WEB API BINDING — WHAT ACTUALLY DIFFERS

Purpose

  • UI: feeds a Fiori Elements or SAPUI5 app used by people.
  • Web API: feeds another system or program (A2A, B2B, integration flows).

UI annotations

  • UI: @UI annotations (line items, facets, field groups, header info) are published in $metadata so Fiori Elements can build screens.
  • Web API: UI annotations are not exposed — they are meaningless to a program.

Draft

  • UI: draft is the normal editing model. Keys contain IsActiveEntity; creates produce drafts that must be activated.
  • Web API: use a non-draft projection. Programs send a complete change in one request; draft would force them through Edit/Activate calls for no benefit.

Contract stability

  • UI: the app and service are shipped together, so fields can change with the app.
  • Web API: other teams build against it. Changes must be backward compatible; breaking changes need a new service version.

Access (cloud)

  • UI: IAM app + business catalog + business role for human users.
  • Web API: communication scenario + communication arrangement + communication user for systems.

━━━━━━━━━━━━━━━━━━━━
▶ THE RULE: SEPARATE PROJECTIONS AND SERVICES PER CONSUMER TYPE

Do not bind the UI service definition as a Web API. Build a second projection layer on the same business object:

  • ZA_PurchReq / ZA_PurchReqItem — API projection: only the fields the API contract needs, no UI annotations, no draft.
  • API projection BDEF — "projection;" without "use draft", exposing only the allowed operations (e.g. read, create, approve — but not delete).
  • Service definition ZAPI_PURCHREQ, binding ZAPI_PURCHREQ_O4 of type OData V4 – Web API.

The business object (ZR_PurchReq) and all its logic stay the same. Both services run the same validations, determinations and authorization checks — that is the point of the layered model.

━━━━━━━━━━━━━━━━━━━━
▶ ODATA V4 VS ODATA V2

Prefer OData V4 for new services:

  • It is SAP's strategic protocol for RAP and for Fiori Elements V4 floorplans.
  • Full RAP draft support and current Fiori Elements features.
  • Leaner JSON payloads and a cleaner batch format.

Use OData V2 only when a consumer really requires it:

  • An existing SAPUI5 or Fiori Elements V2 app that is not being rebuilt.
  • A tool or integration adapter in your landscape that only supports V2.

You can add a V2 binding to the same service definition for such a consumer without changing the BO — one more reason to keep definitions and bindings separate.

SERVICE VERSIONS FOR WEB APIs
The service URL contains a version (…/0001/). For an API contract, a breaking change (removing a field, renaming an entity, changing semantics) should not be made in place. Create a new service definition for the new contract and add it as a new version in the binding (the binding editor supports several service versions on current releases), then let consumers move from 0001 to 0002 at their own pace. Non-breaking additions (a new optional field) can stay in the same version.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Create projection views ZA_PurchReq and ZA_PurchReqItem ("provider contract transactional_query", redirected composition) with only the API fields.
  2. Create the API projection BDEF without "use draft", as in the code example.
  3. Create service definition ZAPI_PURCHREQ exposing both.
  4. Create binding ZAPI_PURCHREQ_O4, type OData V4 – Web API. Activate and publish.
  5. Test in /IWFND/GW_CLIENT: keys no longer contain IsActiveEntity; POST creates an active instance directly; the approve action is callable; DELETE returns an error because it is not exposed.
  6. In the cloud, create a communication scenario containing the inbound service of ZAPI_PURCHREQ_O4.

━━━━━━━━━━━━━━━━━━━━
▶ WHAT YOU HAVE AFTER PART 4

One business object serving two different consumers correctly: a draft-based UI service and a stable non-draft Web API. Part 5 shows how to troubleshoot when either of them fails.

Code example

ABAP Code
// ===== API projection root view (no UI annotations, API fields only) =====@AccessControl.authorizationCheck: #NOT_REQUIRED@EndUserText.label: 'Purchase Request - API projection'define root view entity ZA_PurchReq  provider contract transactional_query  as projection on ZR_PurchReq{  key RequestUUID,      RequestID,      Description,      Status,      TotalAmount,      CurrencyCode,      LocalLastChangedAt,      _Item : redirected to composition child ZA_PurchReqItem} define view entity ZA_PurchReqItem  as projection on ZR_PurchReqItem{  key ItemUUID,      RequestUUID,      ItemNo,      ItemText,      Quantity,      Unit,      Price,      CurrencyCode,      LocalLastChangedAt,      _Request : redirected to parent ZA_PurchReq} // ===== API projection BDEF: no "use draft", no delete =====projection;strict ( 2 ); define behavior for ZA_PurchReq alias PurchaseRequest{  use create;  use update;  use action approve;  use action reject;  use association _Item { create; }} define behavior for ZA_PurchReqItem alias Item{  use update;  use association _Request;} // ===== API service definition =====@EndUserText.label: 'Purchase Request - Web API'define service ZAPI_PURCHREQ {  expose ZA_PurchReq     as PurchaseRequest;  expose ZA_PurchReqItem as PurchaseRequestItem;}// Binding: ZAPI_PURCHREQ_O4, type "OData V4 - Web API" // ===== Same BO, two services - compare the calls =====// UI service (draft)://   POST .../zui_purchreq/0001/PurchaseRequest          -> creates a DRAFT//   POST .../PurchaseRequest(RequestUUID=..,IsActiveEntity=false)/...Activate// Web API (no draft)://   POST .../zapi_purchreq/0001/PurchaseRequest         -> creates an ACTIVE instance//   Key: PurchaseRequest(RequestUUID=...)               -> no IsActiveEntity//   DELETE .../PurchaseRequest(...)                     -> rejected: not exposed

Real project scenario

To save time, a team exposed their Fiori UI service definition a second time as an OData V4 Web API and gave it to the integration team. Two problems followed. First, the external system had to create drafts and then call Activate, and when an activation failed, orphan drafts piled up and locked requests for human users. Second, when the UI team renamed a field for a better screen label, the integration broke in production overnight.
The fix was a separate API projection without draft and with only the contract fields, its own service definition ZAPI_… and a Web API binding. The UI could now change freely, and API changes followed a versioning rule: additions allowed in 0001, anything breaking goes into a new version. Orphan drafts stopped because the API no longer created any.

Common mistakes

  • Reusing the UI service definition for a Web API — draft, UI fields and UI-driven changes leak into the integration contract.
  • Exposing draft to system consumers, leading to orphan drafts and lock conflicts.
  • Exposing delete or other operations in the API that the business never agreed to.
  • Choosing OData V2 for new services without a consumer that requires it.
  • Making breaking changes to a published Web API in place instead of adding a new service version.
  • Putting UI annotations on the base BO views, so they appear in every projection.

Best practices

  • One projection + service definition per consumer type: ZC_/ZUI_ for UI, ZA_/ZAPI_ for APIs.
  • UI services: OData V4 – UI with draft. APIs: OData V4 – Web API without draft.
  • Expose only the operations the consumer is allowed to use.
  • Treat Web APIs as contracts: document them, keep them backward compatible, version breaking changes.
  • Add a V2 binding only for a named consumer that needs it, and plan its retirement.

Interview angle

Likely questions: "What is the difference between an OData V4 UI and Web API binding?", "Would you expose draft in an API?", "When would you still use OData V2?", "How do you version a RAP API?"
Strong answer: UI bindings publish UI annotations and are used with draft for Fiori; Web API bindings omit UI annotations and serve system integration, ideally on a separate non-draft projection with a stable, versioned contract. Draft is a UI editing model and adds Edit/Activate overhead and orphan-draft risk for APIs. V2 only for legacy consumers. For versioning, new service definition as a new version in the binding for breaking changes. Explaining that both services share one BO and therefore the same logic shows solid RAP architecture understanding.

Practice this lesson

Quick check

1. A partner system needs to create and approve purchase requests via API. What is the recommended setup?

2. What is published by an OData V4 UI binding but not by an OData V4 Web API binding?

3. A field exists in the projection but the preview shows no column for it. What is the most likely cause?

Interview questions

1. An integration team wants to use your Fiori UI service for system-to-system approvals. Would you let them? What is the difference between an OData V4 UI and Web API binding?

Answer it in your own words first, then compare.

2. When is OData V2 still justified for a RAP service, and how do you introduce a breaking change to a published Web API?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics