07 · Service Definitions

Define CDS services, expose entities, project columns, rename elements, and restrict which operations are exposed.

Lesson 7 — Service Definitions

What you'll learn

How to design CDS services — projecting entities, renaming fields, restricting operations, composing multiple services, and setting up endpoints for different consumers.


What is a CDS service?

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.


Basic projection

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
  };
}

Restricting operations

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

Renaming and excluding fields

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)
  }
}

Adding calculated fields in projection

service CatalogService {
  entity Books as select from db.Books {
    *,                              // all fields
    stock > 0 as inStock : Boolean  // calculated in DB query
  }
}

Multiple services

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;
}

Setting the service path

// 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" } }

Bound actions and functions

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.


Exposing associations (navigation)

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

Service security overview

// 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.


Checkpoint ✓

In Lesson 8 we put this all together and learn how to effectively use cds watch for daily development.

Sign in for progress tracking & Q&A →