This example covers how to use the built-in types from @sap/cds/common, how to define custom CodeLists, and how to use enum types in CDS.
CodeList |
type enum |
|
|---|---|---|
| Storage | Separate database table | No table — type constraint only |
| Values can change at runtime | ✅ via CSV or API | ❌ requires code change |
| Supports name / descr | ✅ built-in | ❌ value only |
| Typical use case | Countries, currencies, genres, status | Fixed constants: direction, priority |
| Frontend dropdown source | GET /Genres | Hardcoded in frontend |
Rule of thumb: if business users need to maintain the options, use CodeList. If the values are fixed at development time and never change, use enum.
my-codelist/
├── db/
│ ├── schema.cds
│ └── data/
│ ├── my.codelist-Genres.csv
│ ├── my.codelist-BookStatus.csv
│ ├── my.codelist-Books.csv
│ ├── sap.common-Currencies.csv
│ ├── sap.common-Countries.csv
│ └── sap.common-Languages.csv
├── srv/
│ ├── cat-service.cds
│ └── cat-service.js
└── package.json
namespace my.codelist;
using {
cuid, managed,
sap.common.CodeList,
sap.common.Countries,
sap.common.Currencies,
sap.common.Languages
} from '@sap/cds/common';
// Custom CodeList: inherits code (key) + name + descr from CodeList aspect
entity Genres : CodeList {
key code : String(20);
}
entity BookStatus : CodeList {
key code : String(10);
}
// Main entity: uses standard types and custom CodeLists
entity Books : cuid, managed {
title : String(200);
price : Decimal(9, 2);
stock : Integer default 0;
// Associations to standard reference entities
// CAP auto-generates foreign keys: currency_code, country_code, language_code
currency : Association to Currencies;
country : Association to Countries;
language : Association to Languages;
// Associations to custom CodeLists
genre : Association to Genres;
status : Association to BookStatus;
}
// Enum type: no database table, values are fixed at development time
type Priority : String enum {
Low = 'LOW';
Medium = 'MEDIUM';
High = 'HIGH';
}
type Rating : Integer enum {
OneStar = 1;
TwoStar = 2;
ThreeStar = 3;
FourStar = 4;
FiveStar = 5;
}
// Entity using enum types
entity Reviews : cuid {
book : Association to Books;
comment : String(1000);
priority : Priority default 'MEDIUM';
rating : Rating;
}code,name,descr
PROG,Programming,Books about software development
ARCH,Architecture,Software architecture and design patterns
MGMT,Management,Project and team management
SCI,Science,Natural sciences and mathematics
code,name,descr
AVAIL,Available,In stock and ready to ship
OOS,Out of Stock,Temporarily unavailable
DISC,Discontinued,No longer available
ID,title,price,stock,currency_code,country_code,language_code,genre_code,status_code
b1000001-0000-0000-0000-000000000001,Clean Code,38.00,10,USD,US,en,PROG,AVAIL
b1000001-0000-0000-0000-000000000002,The Pragmatic Programmer,45.00,5,USD,US,en,PROG,AVAIL
b1000001-0000-0000-0000-000000000003,Domain-Driven Design,50.00,0,USD,US,en,ARCH,OOS
Note: Use the auto-generated FK column names in CSV (
currency_code, notcurrency).
code,name,descr,symbol,minorUnit
USD,US Dollar,United States Dollar,$,2
EUR,Euro,European Euro,€,2
JPY,Japanese Yen,Japanese Yen,¥,0
GBP,British Pound,British Pound Sterling,£,2
CNY,Chinese Yuan,Chinese Renminbi,¥,2
KRW,Korean Won,Korean Won,₩,0
code,name,descr
US,United States,United States of America
DE,Germany,Federal Republic of Germany
JP,Japan,Japan
GB,United Kingdom,United Kingdom of Great Britain
CN,China,People's Republic of China
KR,Korea,Republic of Korea
FR,France,French Republic
code,name,descr
en,English,English
de,German,Deutsch
ja,Japanese,日本語
zh,Chinese,中文
ko,Korean,한국어
fr,French,Français
sap.commonentities (Currencies, Countries, Languages) are not pre-loaded by CAP automatically. You must provide CSV files namedsap.common-{EntityName}.csvindb/data/.
using my.codelist as db from '../db/schema';
service CatalogService {
entity Books as projection on db.Books;
entity Reviews as projection on db.Reviews;
// Custom CodeLists — expose for frontend dropdowns
entity Genres as projection on db.Genres;
entity BookStatus as projection on db.BookStatus;
// Standard reference entities are auto-exposed by CAP
// because Books has Associations to Currencies/Countries/Languages
// No need to declare them here — they appear automatically
}Do NOT manually re-declare
Currencies,Countries, orLanguagesin the service. CAP auto-exposes them via the Associations onBooks. Declaring them again causes a name collision error.
const cds = require('@sap/cds')
module.exports = class CatalogService extends cds.ApplicationService {
async init() {
const { Books, Reviews } = this.entities
// Validate enum values on Review creation
this.before('CREATE', Reviews, req => {
const { rating, priority } = req.data
console.log('rating:', rating, 'priority:', priority)
const validRatings = [1, 2, 3, 4, 5]
if (rating != null && !validRatings.includes(rating)) {
return req.error(400, `rating must be 1-5, received: ${rating}`)
}
const validPriorities = ['LOW', 'MEDIUM', 'HIGH']
if (priority && !validPriorities.includes(priority)) {
return req.error(400, `priority must be LOW/MEDIUM/HIGH, received: ${priority}`)
}
})
// Log FK codes after READ (expand is needed to get full objects)
this.after('READ', Books, books => {
if (!books) return
const list = Array.isArray(books) ? books : [books]
for (const book of list) {
console.log(`"${book.title}" — currency: ${book.currency_code}, genre: ${book.genre_code}`)
}
})
return super.init()
}
}cds watchOn startup, confirm CSV files are loaded:
> init from db/data/sap.common-Currencies.csv
> init from db/data/sap.common-Countries.csv
> init from db/data/sap.common-Languages.csv
> init from db/data/my.codelist-Genres.csv
> init from db/data/my.codelist-Books.csv
GET /odata/v4/catalog/Currencies
GET /odata/v4/catalog/Countries
GET /odata/v4/catalog/Currencies/USDGET /odata/v4/catalog/Books?$expand=genre,currency,country,languageResponse includes full nested objects for each association:
{
"ID": "b1000001-...",
"title": "Clean Code",
"currency": { "code": "USD", "name": "US Dollar", "symbol": "$" },
"country": { "code": "US", "name": "United States" },
"genre": { "code": "PROG","name": "Programming" }
}POST /odata/v4/catalog/Reviews
Content-Type: application/json
{
"book_ID": "b1000001-0000-0000-0000-000000000001",
"comment": "Excellent book!",
"rating": 5,
"priority": "HIGH"
}POST /odata/v4/catalog/Reviews
Content-Type: application/json
{
"book_ID": "b1000001-0000-0000-0000-000000000001",
"comment": "Test",
"rating": 10,
"priority": "URGENT"
}
# → 400 rating must be 1-5sap.common data is NOT pre-loaded — CSV files are required
// ❌ Wrong assumption: CAP loads Currencies/Countries/Languages automatically
// ✅ You must provide:
db/data/sap.common-Currencies.csv
db/data/sap.common-Countries.csv
db/data/sap.common-Languages.csv
Do not manually re-expose auto-exposed entities
// ❌ Causes name collision error
@readonly entity Currencies as projection on db.Currencies;
// ✅ CAP auto-exposes them via Associations — just leave them outUse FK column names in CSV, not the association field name
// ❌ Wrong
currency,country,language
// ✅ Correct — FK columns auto-generated by CAP
currency_code,country_code,language_code
book.currency is undefined without $expand
// currency is only populated when client sends $expand=currency
// The FK code is always available without expand:
console.log(book.currency_code) // "USD" — always present
console.log(book.currency) // undefined unless $expand=currency