Sap odata
23 SAP development skills for Claude Code — ABAP, RAP, CAP, Fiori, BTP, HANA, S/4HANA, Integration Suite and more. Agent Skills Specification compatible.
npx -y skills add efeumutaslan/SAP-SKILLS --skill sap-odataAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
OData protocol skill for SAP — both service design (OData v2/v4 via RAP, CAP, SEGW) and consumption (UI5, Fiori, CAP remote services, external clients). Use when designing OData services, building query options ($filter, $expand, $select, $orderby, $top, $skip, $search, $count), handling CSRF tokens, batch requests, etags, or debugging OData errors. If the user mentions OData, $metadata, OData v2, OData v4, SEGW, service binding, batch request, CSRF, or OData filter syntax, use this skill.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
13.2 KB, as published. Nobody here has run it
OData Protocol for SAP — Design and Consumption
Related Skills
sap-rap-comprehensive— Primary way to build OData v4 services in modern ABAPsap-cap-advanced— Primary way to build OData v4 services in Node.js/Java on BTPsap-ui5-fiori— Biggest OData consumer in the SAP ecosystemsap-integration-suite-advanced— OData in iFlows, API Management, and B2B scenariossap-s4hana-extensibility— Released OData APIs vs custom OData services
Quick Start
Check a service's metadata (what it exposes):
GET /sap/opu/odata/sap/ZMY_SERVICE/$metadata # OData v2
GET /odata/v4/my-service/$metadata # OData v4
Query entities with common options:
GET /Products?$filter=Price gt 100 and Category eq 'Electronics'
&$select=ID,Name,Price
&$expand=Supplier($select=Name,Country)
&$orderby=Name asc
&$top=20&$skip=0
&$count=true
Pick v2 or v4:
| Decision | Choose |
|---|---|
| New greenfield backend | v4 |
| Modern ABAP (RAP), CAP | v4 (RAP v4 is default since 7.54) |
| Legacy SEGW in S/4 on-prem | v2 (some SAP standard services are still v2) |
| Fiori Elements on S/4HANA 2023+ | v4 preferred |
| UI5 consumer for legacy backend | v2 (with sap.ui.model.odata.v2.ODataModel) |
Core Concepts
v2 vs v4 — critical differences
| Feature | OData v2 | OData v4 |
|---|---|---|
| Key syntax | /Products('HT-1001') | /Products(ID='HT-1001') or /Products('HT-1001') |
| Filter on navigation | $filter=Supplier/Name eq 'X' | $filter=Supplier/Name eq 'X' (same) |
| Count | /$count or $inlinecount=allpages | $count=true |
| Batch format | multipart/mixed | application/json |
| Function import | /GetProducts?name='X' | /getProducts(name='X') |
| Action import | POST /SubmitOrder | POST /submitOrder |
| Deep insert | Supported | Supported |
$apply | Not standard | Standard (aggregations) |
| JSON format | Verbose / light | Standard JSON |
| Type system | EDM types | EDM types (extended) |
Entity Data Model (EDM) types
| Type | SQL equivalent | Notes |
|---|---|---|
Edm.String | VARCHAR/NVARCHAR | MaxLength, Unicode facets |
Edm.Int32 | INT | 32-bit signed |
Edm.Int64 | BIGINT | 64-bit signed |
Edm.Decimal | DECIMAL | Precision, Scale facets |
Edm.Double | DOUBLE | 64-bit float |
Edm.Boolean | BOOLEAN | true/false |
Edm.DateTimeOffset | TIMESTAMP WITH TZ | ISO 8601 with offset |
Edm.Date | DATE | v4 only (v2 uses DateTime) |
Edm.TimeOfDay | TIME | v4 only |
Edm.Guid | UUID | Without hyphens in URLs |
Edm.Binary | BLOB | Base64 in JSON |
Service document structure
/ # Service document — lists entity sets
/$metadata # EDMX — complete service description
/Products # Entity set
/Products('HT-1001') # Entity
/Products('HT-1001')/Name # Primitive property
/Products('HT-1001')/Supplier # Navigation property
/Products/$count # v4: entity count
Common Patterns
Pattern 1: Query options reference
# Select only specific fields (reduces payload)
?$select=ID,Name,Price
# Include related entities (JOIN)
?$expand=Supplier,Category
?$expand=Supplier($select=Name,Country) # nested select
?$expand=Orders($filter=Status eq 'Open') # nested filter (v4)
# Filter
?$filter=Price gt 100
?$filter=contains(Name, 'Pro') # v4: contains, startswith, endswith
?$filter=substringof('Pro', Name) # v2: substringof
?$filter=Category eq 'Electronics' and Price le 500
?$filter=Date ge 2026-01-01 and Date lt 2026-02-01 # v4 date
?$filter=Supplier/Country eq 'DE' # navigation filter
# Sort
?$orderby=Name asc
?$orderby=Price desc, Name asc
# Pagination
?$top=20&$skip=40 # rows 41-60
# Count
?$count=true # v4
?$inlinecount=allpages # v2
# Search (v4, if service supports Capabilities.SearchRestrictions.Searchable)
?$search=laptop
# Aggregations (v4 with $apply)
?$apply=groupby((Category), aggregate(Price with sum as TotalPrice))
Pattern 2: CRUD operations
# CREATE
POST /Products
Content-Type: application/json
X-CSRF-Token: <token>
{
"ID": "HT-9999",
"Name": "New Product",
"Price": 299.99
}
# READ
GET /Products('HT-1001')
# UPDATE (full replace)
PUT /Products('HT-1001')
Content-Type: application/json
If-Match: W/"etag-value"
{ "ID": "HT-1001", "Name": "Updated", "Price": 199.99 }
# PATCH (partial update) — preferred
PATCH /Products('HT-1001')
Content-Type: application/json
If-Match: W/"etag-value"
{ "Price": 199.99 }
# DELETE
DELETE /Products('HT-1001')
If-Match: W/"etag-value"
Pattern 3: Batch request (v4 JSON batch)
POST /$batch
Content-Type: application/json
{
"requests": [
{ "id": "1", "method": "GET", "url": "Products('HT-1001')" },
{ "id": "2", "method": "GET", "url": "Suppliers('S-001')" },
{
"id": "3",
"method": "PATCH",
"url": "Products('HT-1001')",
"headers": { "Content-Type": "application/json", "If-Match": "*" },
"body": { "Price": 199.99 },
"atomicityGroup": "tx1"
}
]
}
Pattern 4: CSRF token flow (write operations)
# Step 1: Fetch token via any GET
GET /sap/opu/odata/sap/ZMY_SERVICE/
X-CSRF-Token: Fetch
# Response headers include:
X-CSRF-Token: abc123xyz
Set-Cookie: sap-XSRF_...
# Step 2: Use token + cookie in subsequent writes
POST /Products
X-CSRF-Token: abc123xyz
Cookie: sap-XSRF_...
UI5's ODataModel handles this automatically. Custom fetch clients must do it manually.
Pattern 5: CAP OData service
// srv/products-service.cds
using { my.db.Products } from '../db/schema';
service ProductsService @(path: '/products') {
entity Products as projection on my.db.Products;
// Action
action approveProduct(ID: String) returns String;
// Function
function productsOverStock(threshold: Integer) returns array of Products;
}
Exposes GET /products/Products, POST /products/approveProduct, etc. — automatically OData v4.
Pattern 6: RAP projection as OData service
" Service definition (ZSD_PRODUCTS)
@EndUserText.label: 'Products Service'
define service ZSD_PRODUCTS {
expose ZC_PRODUCT as Products;
expose ZC_SUPPLIER as Suppliers;
}
" Service binding (ZSB_PRODUCTS_V4)
" Binding Type: OData V4 - UI
" Service Definition: ZSD_PRODUCTS
Published at /sap/opu/odata4/sap/zsb_products_v4/srvd_a2x/sap/zsd_products/0001/.
Pattern 7: UI5 consumption
// OData v4
const oModel = new sap.ui.model.odata.v4.ODataModel({
serviceUrl: "/odata/v4/products/",
synchronizationMode: "None",
operationMode: "Server",
autoExpandSelect: true
});
// Read
const oContext = oModel.bindContext("/Products('HT-1001')");
oContext.requestObject().then(oProduct => console.log(oProduct));
// Update (PATCH)
oContext.setProperty("Price", 199.99);
await oModel.submitBatch("$auto");
// Create
const oListBinding = oModel.bindList("/Products");
const oNewContext = oListBinding.create({ ID: "HT-9999", Name: "New" });
await oNewContext.created();
Pattern 8: External client (fetch/axios)
// Fetch CSRF token
const tokenRes = await fetch("/sap/opu/odata/sap/ZMY_SERVICE/", {
headers: { "X-CSRF-Token": "Fetch" },
credentials: "include"
});
const csrfToken = tokenRes.headers.get("x-csrf-token");
// Write with token
const createRes = await fetch("/sap/opu/odata/sap/ZMY_SERVICE/Products", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken,
"Accept": "application/json"
},
body: JSON.stringify({ ID: "HT-9999", Name: "New" })
});
Error Catalog
| HTTP | Error | Cause | Fix |
|---|---|---|---|
| 400 | Invalid filter expression | Wrong filter syntax or unknown property | Check property name case; eq/ne/gt/lt must be lowercase; strings need single quotes |
| 400 | Could not parse URI | Invalid key syntax, wrong v2 vs v4 | v2: /Products('HT-1001'); v4: /Products(ID='HT-1001') or /Products('HT-1001') |
| 401 | Authentication failed | Missing or invalid credentials | Add Basic Auth, form login, or OAuth token |
| 403 | CSRF token validation failed | Missing CSRF token on write | Fetch token via GET with X-CSRF-Token: Fetch, reuse on POST/PUT/PATCH/DELETE |
| 403 | Not authorized | User lacks permissions | Check role (PFCG for ABAP, XSUAA scopes for CAP/BTP) |
| 404 | Resource not found | Wrong URL or service not active | Check service is published; verify path; check /$metadata loads |
| 412 | Precondition Failed | Missing or stale If-Match etag | Include If-Match: <etag> from the last read |
| 428 | Precondition Required | etag required but not sent | Send If-Match: * to force update without etag check (if allowed) |
| 501 | Not Implemented | Function/feature not supported by service | Check $metadata — does the service declare the capability? |
| 500 | /IWBEP/CX_MGW_BUSI_EXCEPTION | Business logic error in SEGW data provider | Read the inner message; check backend logs (ST22 for ABAP dumps) |
Performance Tips
- Always
$selectwhat you need — never fetch full entities when you only need 3 columns. $expandis expensive — prefer a second batch request over deep expands.- Server-side paging with
$top+$skip— never load all rows; clients like UI5 do this automatically withgrowing. $count=true— only request the count when the UI actually shows it.- Batch write operations — one
$batchinstead of N single calls reduces round-trip time dramatically. - Use
PATCH, notPUT— PATCH sends only changed fields, PUT sends the whole entity. - Index navigation property columns —
$filteron joined fields otherwise does full scans. $applyfor aggregations (v4) — let the server aggregate instead of fetching raw rows.- Cache
$metadata— it rarely changes; UI5 does this automatically. - Disable cursors for small result sets — OData v4
Capabilities.TopSupportedcan be tuned. - Projection CDS views — at the DB layer, expose only the fields needed by OData.
Gotchas
- Single quotes for strings, no quotes for numbers —
$filter=Name eq 'X'vs$filter=Price eq 100. - Date literals differ — v2:
datetime'2026-01-01T00:00:00'; v4:2026-01-01(Date) or2026-01-01T00:00:00Z(DateTimeOffset). - URL encoding —
$filter=Name eq 'O''Brien'(apostrophe doubled), spaces as%20. $inlinecountvs$count— don't mix up;$inlinecount=allpagesis v2,$count=trueis v4.- Navigation property case sensitivity —
$expand=Supplier≠$expand=supplierin most services. - Action names — v2 uses
GET+ function imports for side-effect-free,POSTotherwise; v4 clearly separates Actions (POST) from Functions (GET). - Deep insert depth — most backends limit to 2–3 levels.
- Batch changesets — atomic unit in v2
$batch; mistakes in one operation roll back the whole changeset. - Lambda operators (
any,all) — v4 only; v2 services ignore or error. - Key predicates with multiple fields —
/Orders(OrderID=5,ItemID=1)— order matters in some implementations.
Debugging Workflow
- Always check
$metadatafirst — it's the source of truth for what's exposed. - Use the Gateway Client (
/IWFND/GW_CLIENT) on ABAP — test requests with full headers/body. - Browser DevTools → Network → XHR filter — see the exact URL, headers, and response.
- Postman/Insomnia — build requests manually, inspect auth headers.
- Enable trace in backend — ABAP:
/IWFND/TRACES; CAP:DEBUG=odata*. - Metadata viewer tools — odata-debugger shows the service structure.
References
references/query-options-cheatsheet.md— All query options with examplesreferences/edm-types-reference.md— EDM type catalog with literals and facetsreferences/odata-v2-vs-v4.md— Side-by-side comparison for migrationtemplates/cap-service-template.cds— CAP OData service skeletontemplates/rap-service-definition.asddls— RAP service definition template
MCP Server Integration
For OData development:
@cap-js/mcp-server— CAP service design, OData metadata, CDS authoringmarianfoo/mcp-sap-docs— SAP documentation lookup (OData SDK, ABAP OData)- Vibing Steampunk MCP — ABAP Gateway service activation, SEGW operations
Install via mcp-configs/fullstack-btp.json.