Aspects
An aspect is a reusable collection of fields, annotations, or relationships that can be shared across multiple entities to avoid writing the same code repeatedly.
We use aspects to reuse common fields, annotations, or relationships across multiple entities, reducing code duplication and making the data model easier to maintain.
1. uuid
- UUID (Universally Unique Identifier) is a data type in SAP CAP that stores a globally unique 128-bit identifier.
- It is used when you want every record to have a unique ID that is extremely unlikely to duplicate, even across different systems.
We can use UUID in entity like -
entity Warehouse {
key warehouseID : UUID;
name : String;
owner : String;
address : String;
}2. cuid
- cuid (Canonical Unique Identifier) is a built-in aspect provided by SAP CAP that automatically adds a primary key named ID of type UUID to an entity.
- It saves you from manually declaring the UUID key, so if we use cuid then entity will itself make a field of ID as UUID and its value will automatically gets added when we add new entry on the entity.
We can use CUID aspect in entity like -
using { cuid } from '@sap/cds/common';
entity Warehouse : cuid{
name : String;
owner : String;
address : String;
}and the actual entity internally will look like -
entity Warehouse {
key ID : UUID;
name : String;
owner : String;
address : String;
}So we do not need to add or manage the ID manually.
3. managed
- managed is a built-in aspect that automatically adds and maintains audit fields such as who created or modified a record and when those actions occurred.
We can use this aspect in entity like -
using { cuid, managed } from '@sap/cds/common';
entity Warehouse : cuid, managed{
name : String;
owner : String;
address : String;
}and the actual entity internally will look like -
entity Warehouse {
key ID : UUID;
name : String;
owner : String;
address : String;
createdAt : Timestamp;
createdBy : User;
modifiedAt : Timestamp;
modifiedBy : User;
}4. temporal
- temporal is a built-in aspect that adds validity period fields (validFrom and validTo) to an entity.
- It is used for storing historical or time-dependent data.
We can use this aspect in entity like -
using { cuid, temporal } from '@sap/cds/common';
entity Warehouse : cuid, temporal{
name : String;
owner : String;
address : String;
}and the actual entity internally will look like -
entity Warehouse {
key ID : UUID;
name : String;
owner : String;
address : String;
validFrom : Timestamp;
validTo : Timestamp;
}5. CodeList
- A CodeList in SAP CAP is a predefined aspect used to create lookup (master) data.
- It stores a fixed list of values that can be reused across your application instead of typing the same text repeatedly.
- For Example, if we are repeatedly using the Region to define country, we can instead define it in a Region entity and rest other entity can use it simply.
Aspect CodeList actually is -
aspect CodeList {
key code : String;
name : String;
descr : String;
}and we can use it on entity as -
using { cuid, sap.common.CodeList as CodeList} from '@sap/cds/common';
entity Regions : CodeList {};
entity Warehouse : cuid {
name : String;
owner : String;
address : String;
region : Association to Regions;
}6. Custom Aspect
- A custom aspect is a reusable collection of fields, associations, or annotations created by the developer.
- It allows the same structure to be shared across multiple entities.
We can create an aspect like -
aspect SoftDelete {
isDeleted : Boolean default false;
}and can use it on entity like -
using { cuid } from '@sap/cds/common';
aspect SoftDelete {
isDeleted : Boolean default false;
}
entity Warehouse : cuid, SoftDelete {
name : String;
owner : String;
address : String;
}and our entity will actually looks like -
entity Warehouse : cuid, SoftDelete {
name : String;
owner : String;
address : String;
isDeleted : Boolean default false;
}Code Example using all the aspects -
Let's create an application to manage warehouse details.
- The application should maintain audit information, such as when a record was created or last updated - for which we will use managed.
- It should also store the validity period of each warehouse - for which we will use temporal.
- It also defines the country or region to which the warehouse belongs - for which we will use Region CodeList.
- It also keep track of whether a warehouse record has been marked or requested for deletion - for which we will define our custom aspect.
So, we can define the entity with aspects in schema.cds file in db folder like -
namespace cap.application.db.schema;
using { cuid, managed, temporal, sap.common.CodeList as CodeList } from '@sap/cds/common';
aspect SoftDelete {
isDeleted : Boolean default false;
}
entity Region : cuid, CodeList {};
entity Warehouse : managed, temporal, SoftDelete {
key ID : UUID;
name : String;
owner : String;
address : String;
region : Association to Region ;
}We can then create entity data csv file using command -
cds add dataWe can then define some sample data for Country Region like -
ID,name,descr
550e8400-e29b-41d4-a716-446655440001,India,Country-India
550e8400-e29b-41d4-a716-446655440002,Germany,Country-Germany
550e8400-e29b-41d4-a716-446655440003,United States,Country-United States
550e8400-e29b-41d4-a716-446655440004,Japan,Country-Japan
550e8400-e29b-41d4-a716-446655440005,Australia,Country-AustraliaWe can define the service file like -
using {cap.application.db.schema} from '../db/schema';
service WarehouseService @(path: 'warehouse') {
entity Warehouses as projection on schema.Warehouse;
function AllEntities() returns String ;
}And similarly we can define our custom logic to store warehouse details like -
import cds from '@sap/cds';
// refering to the entity which we have defined in our data model. We can also refer to the entity by using the relative path like this : cds.entities('Warehouses')
// but it is always recommended to use the absolute path to avoid any confusion in case of multiple entities with same name in different namespaces.
// const { Warehouses } = cds.entities('warehouse'); // -> Relative path to refer to the entity
const { Warehouse } = cds.entities('cap.application.db.schema'); // -> Absolute path to refer to the entity (RECOMMENDED).
const WarehouseService = async (srv) => {
// ######################### Handler to get all the Entity details ##########################
srv.on('AllEntities', async (request) => {
console.log(Object.keys(cds.entities('cap.application.db.schema')));
});
// ######################### CREATE - Handler ##########################
srv.before('CREATE', 'Warehouses', async (request) => {
try {
const { name, owner, address } = request.data;
if (!name || !owner || !address) {
return request.error({
code: 400,
message: 'Invalid request, please check your payload'
});
}
// We wants to modify the owner field and wants to append it with "- NEW" just for fun ;-)
request.data.owner = `${request.data.owner} - NEW`;
}
catch (error) {
return request.error({
code: 500,
message: error.message
});
}
});
srv.on('CREATE', 'Warehouses', async (request) => {
try {
const { name, owner, address, region_ID } = request.data;
// Lets make it valid from today and valid to 1 year from now
const newEntryPayload = {
name: name,
owner: owner,
address: address,
region_ID: region_ID,
validFrom: new Date().toISOString(),
validTo: new Date(new Date().setFullYear(new Date().getFullYear() + 1)).toISOString()
}
const newEntry = await cds.tx( async (tx) => {
return await tx.run(INSERT.into(Warehouse).entries(newEntryPayload));
});
if (!newEntry) {
return request.error({
code: 500,
message: 'Something went wrong while creating the warehouse entry'
});
}
return newEntryPayload;
}
catch (error) {
return request.error({
code: 500,
message: error.message
});
}
});
srv.after('CREATE', 'Warehouses', async (data, request) => {
// Just for fun, we wants to log the message in console after creating the entry in database.
console.log(data);
console.log("Successfully created the Record");
return data;
});
}
export default WarehouseService;To test the endpoint locally, run the application and create http file like -
### GET call to Read all the Warehouses
GET http://localhost:4004/odata/v4/warehouse/AllEntities
### GET call to Read all the Warehouses
GET http://localhost:4004/odata/v4/warehouse/Warehouses
### POST call to Create a new Warehouse
POST http://localhost:4004/odata/v4/warehouse/Warehouses
Content-Type: application/json
{
"name" : "Elite Storage",
"owner" : "Elite Transport Co",
"address" : "77 Business Zone Panchkula",
"region_ID" : "550e8400-e29b-41d4-a716-446655440005"
}!!! Its Done !!!