Define CDS services, expose entities, project columns, rename elements, and restrict which operations are exposed.
How to design CDS services — projecting entities, renaming fields, restricting operations, composing multiple services, and setting up endpoints for different consumers.
A CDS service is an OData V4 service that exposes your data model to clients. Think of it as the "API contract" — it defines exactly what clients can see and do.
db/schema.cds ← Internal data model (full database schema)
│
▼
srv/my-service.cds ← Service = filtered, projected view of that model
│
▼
OData V4 API ← What clients (Fiori, mobile, REST) consume
The service layer is intentionally separate from the data model. Your database can have 100 fields on an entity — you only expose the 20 that external consumers need.
using com.bookshop as db from '../db/schema';
service CatalogService {
entity Books as projection on db.Books;
}
This exposes all fields of Books. To control what's exposed, use a SELECT-style projection:
service CatalogService {
entity Books as select from db.Books {
ID,
title,
price,
author.name as authorName // flatten association
};
}
service CatalogService {
// Customers can only read books
@readonly
entity Books as projection on db.Books;
// Orders can be created and read, not updated or deleted
@insertonly
entity Orders as projection on db.Orders;
}
service AdminService @(requires: 'admin') {
// Admins get full CRUD
entity Books as projection on db.Books;
}
| Annotation | Allowed operations |
|---|---|
@readonly |
GET only |
@insertonly |
POST only |
| (none) | Full CRUD |
service CatalogService {
entity Books as select from db.Books {
ID,
title,
price,
// Rename a field
stock as availableCount,
// Exclude internal fields by not listing them
// (internalCode is not included → not exposed)
}
}
service CatalogService {
entity Books as select from db.Books {
*, // all fields
stock > 0 as inStock : Boolean // calculated in DB query
}
}
It's common to have multiple services in one CAP project — one per consumer group:
// srv/catalog-service.cds — Public API
service CatalogService {
@readonly entity Books as projection on db.Books;
@readonly entity Authors as projection on db.Authors;
entity Orders as projection on db.Orders
where $user.id = createdBy; // row-level filter
}
// srv/admin-service.cds — Back-office
@(requires: 'admin')
service AdminService {
entity Books as projection on db.Books;
entity Authors as projection on db.Authors;
entity Users as projection on db.Users;
}
// Default path: /odata/v4/catalogservice
service CatalogService { ... }
// Custom path: /api/v1/catalog
@(path: '/api/v1/catalog')
service CatalogService { ... }
// Prefix all services via .cdsrc.json:
// { "odata": { "version": 4 }, "server": { "basePath": "/api" } }
Actions and functions are service-level operations (like RPC calls):
service OrderService {
entity Orders as projection on db.Orders;
// Bound action — acts on a specific Order
action submitOrder() returns Order;
// Bound function — reads from a specific Order
function getTracking() returns String;
// Unbound action — not tied to an entity instance
action cancelAllPending() returns Integer;
// Unbound function
function getOrderStats() returns {
total : Integer;
pending : Integer;
shipped : Integer;
};
}
Actions = POST (state-changing), Functions = GET (read-only)
We implement handlers for these in Lesson 25.
OData supports navigation — traversing from one entity to another:
service CatalogService {
entity Books as projection on db.Books {
*,
author: redirected to Authors // allow navigation to Author
};
entity Authors as projection on db.Authors;
}
Client can then use:
GET /odata/v4/catalog/Books(1234)?$expand=author
GET /odata/v4/catalog/Authors(5678)/books
// Entire service requires a role
@(requires: 'authenticated-user')
service MyService { ... }
// Per-entity requirements
service MyService {
@(requires: 'admin')
entity Users as projection on db.Users;
@readonly
@(requires: 'viewer')
entity Products as projection on db.Products;
}
Full authorization (XSUAA, @restrict) is covered in Module 5.
In Lesson 8 we put this all together and learn how to effectively use cds watch for daily development.