SAP OData in ABAP
OData is the RESTful protocol SAP uses to expose data and logic to web and mobile clients, above all to Fiori apps. In modern ABAP you create OData services (increasingly from CDS) so front-ends can consume backend data over HTTP/JSON.
An OData service is how a Fiori app, or any client, reads and updates ABAP data over HTTP as JSON, with query options such as $filter, $select, $expand and $top. In S/4HANA you build one by annotating a CDS view or through a RAP service definition, and activate it in the gateway before it can be called.
- Watch out: Hand-building services when CDS-based exposure is easier.
What OData is
OData (Open Data Protocol) is a standardised REST protocol for querying and updating data over HTTP, returning JSON (or XML). Every Fiori app talks to the backend via an OData service. OData supports the CRUD operations plus rich query options (filter, sort, expand, paging), so clients can request exactly the data they need.
One property is worth calling out because it is what makes the protocol useful: a service describes itself. The metadata document lists every entity, every field, its type and the relationships between them, so a client can be built against a service it has never seen without documentation. That is why Fiori apps and external tools can consume SAP data without a bespoke integration for each one.
How OData services are built
- From CDS (recommended): annotate a CDS view with @OData.publish or expose it via a service definition/binding (RAP), the modern, low-effort path.
- SAP Gateway Service Builder (SEGW): the classic way, define an entity model and implement the operations in generated ABAP classes.
A typical OData request
GET /sap/opu/odata/sap/Z_SALES_SRV/SalesOrderSet?$filter=Customer eq '1000'&$top=20
Authorization: <session/OAuth>The query options, and why they matter for performance
The system query options are what make OData more than a URL, and understanding where they execute is the difference between a fast service and a slow one.
$filter restricts rows, $select restricts columns, $top and
$skip page, $orderby sorts, $expand pulls in related entities,
$count returns a total, and $search does free text.
The critical question for each is whether it reaches the database. In a CDS-based service they generally do: the framework translates them into the generated SQL, so a filter genuinely reduces what is read. In a hand-built service they do not unless the developer implemented them, and the common failure is a service that reads everything into memory and then applies the filter in ABAP. That service works, passes a test with fifty records, and falls over on fifty thousand.
$expand deserves particular care. Expanding a collection of related entities can multiply
the result enormously, and expanding two levels deep on a list is how a Fiori app becomes slow the day
real data arrives.
The habit worth building: when a service is slow, trace it and see what statement reached the database. If the filter is missing from that statement, the framework did not push it down and that is the problem to fix.
Expose a CDS view and call it
Forty minutes, and it is the shortest path from a table to a working API.
- Create a CDS view over a table with a handful of fields and activate it.
- Add the annotation that marks it for OData exposure and activate again.
- Activate the generated service in the gateway. Until this is done the service exists and is not reachable, which is the most common "my service does not work" cause.
- Open the service document in a browser and read the metadata. Every entity, field and type is described there, which is what makes OData self-describing.
- Call it with
$top=10and then with a$filter. Confirm both return what you expect. - Trace it and read the SQL. The filter should be in the statement rather than applied afterwards.
Why it matters
OData is the bridge between ABAP/CDS on the backend and Fiori/UI5 (and external apps) on the front-end. Understanding how to expose and consume OData is essential for any modern SAP development involving user interfaces or integration with web systems.
It is also the boundary where clean core is enforced. An extension built against a released OData service survives an upgrade, because SAP maintains the contract. One built against internal tables does not. That is why the direction of travel is exposing and consuming services rather than reading tables directly, even inside the same system.
The decisions when building a service
- CDS-based or hand-built. CDS first, always, unless the requirement genuinely cannot be modelled. Hand-built means implementing every query option yourself and maintaining it.
- Which OData version. V2 is widespread and V4 is the current standard with better capability. New development generally goes to V4, and existing Fiori apps constrain the choice.
- Read only or writable. Exposing updates means implementing the behaviour and the validation behind it, which is a considerably larger commitment than a read.
- How it is secured. A service inherits authorisation from what it reads only if the underlying view has access control. See Fiori and OData.
RAP, and where OData is heading
Exposing a CDS view read-only is straightforward. Building a service that also creates and changes data used to mean implementing a great deal by hand, and the ABAP RESTful Application Programming model is what replaced that.
In RAP, the data model is CDS views, and a behaviour definition declares what can be done to them: which operations are allowed, what validations run, what determinations fill fields automatically, and what actions are available. A behaviour implementation in ABAP supplies the logic those declarations reference.
The framework then generates the OData service, handles the transactional buffer, and enforces the declared rules. What a developer writes is the business logic rather than the plumbing.
Two flavours exist. Managed lets the framework handle persistence, which suits new tables. Unmanaged is for wrapping existing logic that already owns its own saving, which is how legacy functionality is exposed without rewriting it.
The relevance for anybody learning OData now: read-only exposure through annotations is the starting point, and anything writable in a current system is likely to be RAP rather than hand-built gateway code.
Debugging a service that does not work
Services fail in a small number of ways and the order of checks resolves most of them quickly.
Is it activated. The service definition existing is not the same as the service being registered and active in the gateway. This is the most common cause and the quickest to check.
Does the metadata load. Requesting the metadata document exercises the service without touching business data. If that fails, the problem is the definition rather than the logic.
Is there a cache. The gateway caches metadata, and a changed service that still behaves as it did is usually a cache that needs clearing. Everyone loses an hour to this once.
What does the error actually say. Gateway errors are logged with detail, and the message returned to the client is often a summary. Reading the log rather than the response is the difference between a guess and an answer.
Does the user have authorisation. A service returning nothing rather than failing is frequently a user who can call it and cannot see the data.
Working through those five in order covers the large majority, and the remainder are logic problems best found by tracing the request.
Common pitfalls
- Hand-building services when CDS-based exposure is easier.
- Returning too much data, use $filter/$top and paging.
- Ignoring OData error handling in the service implementation.
- Forgetting to activate the service in the gateway. The definition exists and nothing can call it.
- Filters applied in ABAP after reading everything. It works until the data grows.
- Deep expands on a list. See SAP OData services and ABAP data types for what the fields become.
- No paging on a collection. A client that requests everything will, and a service that obliges will eventually be asked for a million records.
Where this goes next
Exposing a view is straightforward, and building a service that stays fast and secure when real data arrives is the part you do in the course.
The check worth running on any service before it ships is a trace on a filtered request. If the filter did not reach the database, the service will be fine in testing and slow the first week it meets real data.