OData V2 Part 1: The Classic OData V2 Service Model
Understand the classic OData V2 service model you will meet in existing SAP landscapes: service document, $metadata, entity sets, associations and navigation, V2 query syntax, JSON format and how V2 differs from V4.
What this complete series covers
Learn OData V2 in a clear, practical sequence — from the basic idea to real project use, troubleshooting, and interview-ready understanding.
- Who it is for
- Best for SAP consultants, developers, and learners who want a practical understanding of OData V2.
- Prerequisites
- Basic ABAP and ADT in Eclipse are recommended. The series explains the RAP/CDS/OData concepts as they are introduced.
- Final outcome
- By the end, you should be able to explain OData V2 clearly, follow the main end-to-end flow, apply it in a project scenario, and troubleshoot the most common issues.
- Part 1: OData V2 Part 1: The Classic OData V2 Service Model
Understand classic SAP OData V2 as an HTTP service model with metadata, entity sets, keys, CRUD operations and standard query options that Fiori and external clients can consume.
- Part 2: OData V2 Part 2: Deep Insert, Batch and Navigation Design
Use navigation, deep insert and $batch for different service needs, preserving business transaction boundaries and avoiding large chatty or over-expanded APIs.
- Part 3: OData V2 Part 3: Production Troubleshooting — Gateway Logs and Metadata
Troubleshoot OData V2 by reproducing the exact request, verifying metadata and backend user, then isolating Gateway protocol, authorization, application and database failures with the proper logs.
System and release assumptions: Examples target recent SAP S/4HANA / ABAP Cloud capabilities. RAP, CDS, OData, and Fiori Elements syntax and tooling can differ by ABAP platform release.
Explanation
📌 KEY POINTS
✅ OData V2 is still everywhere: SEGW services, many standard Fiori apps, older UI5 and integrations.
✅ V2 URLs live under /sap/opu/odata/sap/<SERVICE>/; RAP can also publish V2 bindings.
✅ Syntax differs from V4: $inlinecount=allpages, substringof(), JSON wrapped in "d".
💡 New RAP services use V4; use V2 only for consumers that require it.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT THIS SERIES COVERS
- Part 1 — the classic OData V2 service model (this lesson)
- Part 2 — deep insert, batch and navigation design
- Part 3 — production troubleshooting with gateway logs and metadata
Related: SAP Gateway series (the runtime behind V2 services).
━━━━━━━━━━━━━━━━━━━━
▶ 1. WHERE YOU MEET V2
- Services built with transaction SEGW (Gateway Service Builder).
- Many SAP-delivered Fiori apps and older custom UI5 apps (sap.ui.model.odata.v2.ODataModel).
- Integrations written years ago.
- RAP service bindings of type "OData V2 – UI" or "OData V2 – Web API" for consumers that cannot use V4.
━━━━━━━━━━━━━━━━━━━━
▶ 2. URL AND CONTRACT
- Service root: /sap/opu/odata/sap/ZPURCHREQ_SRV/ (SEGW) or the binding URL for RAP V2.
- $metadata: EntityType, EntitySet, Association, AssociationSet, NavigationProperty, FunctionImport — and SAP annotations (sap:label, sap:creatable, sap:filterable…).
- Keys: /PurchaseRequestSet('4711') or /PurchaseRequestSet(RequestID='4711',Year='2026').
━━━━━━━━━━━━━━━━━━━━
▶ 3. V2 QUERY OPTIONS
- $select, $filter, $orderby, $top, $skip, $expand — like V4 but simpler ($expand without nested options).
- $inlinecount=allpages — total count in the response (V4: $count=true).
- Functions: substringof('chair',Description) (V4: contains), startswith, endswith.
- Dates in filters: datetime'2026-09-01T00:00:00'.
━━━━━━━━━━━━━━━━━━━━
▶ 4. JSON FORMAT
Responses (with $format=json or Accept: application/json) are wrapped in "d":
{ "d": { "results": [ … ], "count": "57" } } for lists; { "d": { … } } for single entities, with "metadata" (uri, type, etag).
━━━━━━━━━━━━━━━━━━━━
▶ 5. OPERATIONS
- CRUD: GET, POST, PUT (full update) and MERGE/PATCH (partial update), DELETE.
- Function imports for operations (V4: actions/functions).
- CSRF token required for writes (same as V4).
━━━━━━━━━━━━━━━━━━━━
▶ 6. V2 VS V4 (WHAT CHANGES FOR YOU)
- V4: cleaner JSON, contains(), $count, nested $expand options, bound actions, better batch.
- V2: mature tooling, many existing apps; function imports instead of actions.
- Migration of a consumer from V2 to V4 means changing the UI5 model and request syntax — plan it as a project.
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Open an existing V2 service in /IWFND/GW_CLIENT (or a browser) and read $metadata.
- Run the V2 queries in the code example.
- Compare the same query against the RAP V4 service.
✅ CHECKPOINT: you can read and query any SAP OData V2 service.
Code example
### {v2} = /sap/opu/odata/sap/ZPURCHREQ_SRV (SEGW service)### RAP V2 binding: /sap/opu/odata/sap/ZUI_PURCHREQ_O2/ ### ContractGET {v2}/$metadata ### List with fields, filter, sort, paging and total count (V2 syntax)GET {v2}/PurchaseRequestSet?$select=RequestID,Description,TotalAmount,Currency &$filter=Status eq 'S' and substringof('chair',Description) &$orderby=TotalAmount desc&$top=20&$skip=0 &$inlinecount=allpages&$format=json ### Response (V2 JSON: wrapped in "d")# { "d": { "__count": "57",# "results": [ { "__metadata": { "uri": ".../PurchaseRequestSet('4711')",# "type": "ZPURCHREQ_SRV.PurchaseRequest",# "etag": "W/\"datetime'2026-09-24T09%3A00%3A00'\"" },# "RequestID": "4711", "Description": "Office chairs", ... } ] } } ### Single entity and navigationGET {v2}/PurchaseRequestSet('4711')?$format=jsonGET {v2}/PurchaseRequestSet('4711')/ToItems?$format=jsonGET {v2}/PurchaseRequestSet('4711')?$expand=ToItems&$format=json ### Date filter in V2GET {v2}/PurchaseRequestSet?$filter=CreatedOn ge datetime'2026-09-01T00:00:00' ### Function import (V2 "operation")POST {v2}/ApproveRequest?RequestID='4711'X-CSRF-Token: <token> ### V2 -> V4 cheat sheet# $inlinecount=allpages -> $count=true# substringof('x',F) -> contains(F,'x')# {"d":{"results":[]}} -> {"value":[]}# function import -> bound action / function# MERGE -> PATCHReal project scenario
A new developer familiar only with RAP V4 tried to add a filter to an old SEGW-based integration using contains(Description,'chair') and $count=true. The service returned errors, and he suspected a broken backend.
A colleague showed him the V2 equivalents — substringof and $inlinecount=allpages — and the "d" wrapper in responses. The integration worked immediately. The team then added a V2/V4 cheat sheet to their wiki, since both protocols would coexist for years.
Common mistakes
- Using V4 syntax against V2 services (contains, $count).
- Forgetting the "d" wrapper when parsing V2 JSON.
- Using PUT for partial updates (overwrites unspecified fields).
- Not requesting JSON ($format=json / Accept header).
- Building new RAP services as V2 without a consumer reason.
Best practices
- Know which protocol a consumer uses before writing requests.
- Read $metadata and SAP annotations (sap:creatable, sap:filterable) first.
- Use MERGE/PATCH for partial updates.
- Offer V2 bindings for RAP services only when consumers need them.
- Plan V2→V4 migrations deliberately.
Interview angle
Questions: "What are differences between OData V2 and V4?", "Where do SAP OData V2 services come from?", "How do you get a total count in V2?"
Strong answer: V2 services come from SEGW, standard apps and RAP V2 bindings, under /sap/opu/odata/sap/; syntax differences: $inlinecount=allpages vs $count, substringof vs contains, "d"-wrapped JSON, function imports vs actions, MERGE vs PATCH, simpler $expand. New RAP services should use V4 unless a consumer requires V2.
Practice this lesson
Quick check
1. Which V2 filter finds entities whose Description contains "chair"?
2. Which query option returns the total number of matching entities in OData V2?
3. Which HTTP header must a client fetch before sending POST requests to a V2 service?
Interview questions
1. What are the main differences between OData V2 and V4 for an SAP developer?
Answer it in your own words first, then compare.
2. Which Gateway tools do you use to troubleshoot OData V2 incidents, and for what?
Answer it in your own words first, then compare.
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.