CAP Learning Path
Five progressive milestones, each building on the previous one. Every milestone links to the relevant files in this repo.
Milestone 1 — Understand the Domain Model
Goal: Read CDS like a native, understand how types and relationships work.
Tasks:
Open
db/schema.cdsand find:- A custom type with a regex validation (
@assert.format) - An entity using both
cuidandmanagedmixins - An enum with
@assert.range
- A custom type with a regex validation (
Answer these questions:
- What does
Composition of manymean vsAssociation to? - What happens at the database level for a
Composition? - Which entity has
@cds.persistence.journaland why?
- What does
Trace how
Film2PeoplelinksFilmandPeople(M:N relationship).Find the
define viewdeclarations forShow2Planets,Show2Starships,Show2Vehicles, andShow2Species. Explain why they are views instead of physical junction tables. (Hint: where does the underlying data actually come from?)
Key files: db/schema.cds
Check your understanding: Run npm run test — all model tests should pass.
Milestone 2 — Understand Service Projections
Goal: Understand how services convert domain entities into consumer-facing APIs.
Tasks:
Compare
db/schema.cdsPeopleentity with thePeopleprojection insrv/people-service.cds:- What fields are removed or made read-only?
- What is
redirected to? - What does
@odata.draft.enabledenable?
Compare the
Filmentity across two different services:StarWarsFilm.Film(infilm-service.cds) — writableStarWarsPeople.Film(inpeople-service.cds) — readonly- Same entity, different access modes per service.
Call the service via its REST endpoint:
GET http://localhost:4004/rest/StarWarsPeople/PeopleThen via OData:
GET http://localhost:4004/odata/v4/StarWarsPeople/People?$top=3&$expand=homeworld
Key files: srv/people-service.cds, srv/film-service.cds
Check your understanding: Open Swagger UI at http://localhost:4004/api-docs and explore the generated spec. Then compare StarWarsEpisode (srv/episode-service.cds) — a read-only service with no .js handler at all — with StarWarsPeople. Notice that CAP's generic provider handles all CRUD automatically when there is no on handler registered.
Milestone 3 — Service Handlers
Goal: Understand the before → on → after event lifecycle.
Tasks:
Open
srv/people-service.jsand identify:- The
beforehook — what does it validate? - The
onhandler for therenameaction — what does it do? - The
afterhook for READ — what does it compute? - The
afterhook for write events — what does it emit?
- The
Test the
beforevalidation by trying to create a Person with an empty name:httpPOST http://localhost:4004/odata/v4/StarWarsPeople/People Content-Type: application/json { "name": " " }You should get a
400 Bad Requestwith a meaningful message.Call the custom
renameaction:httpPOST http://localhost:4004/odata/v4/StarWarsPeople/People(<ID>)/rename Content-Type: application/json { "newName": "Darth Vader" }Call the unbound
countByGenderfunction:GET http://localhost:4004/odata/v4/StarWarsPeople/countByGender(gender='male')
Key files: srv/people-service.js, srv/people-service.cds
Check your understanding: Complete labs/lab-03-handler/.
Milestone 4 — Authorization
Goal: Understand how CAP enforces roles without writing imperative checks.
Tasks:
Open
srv/services-auth.cdsand understand:- Which services are currently public (
@requires: 'any')? - What does the
@restrictblock onPeopledeclare? - What is the difference between
@requiresand@restrict?
- Which services are currently public (
Enable mock users in
package.json(undercds.requires.auth) and test:- Access as a Viewer: can you READ People?
- Access as an Editor: can you CREATE a new Person?
- Access as an Admin: can you DELETE?
Add a
@requires: 'Admin'annotation to therenameaction inpeople-service.cds. Verify it blocks non-admin calls.
Key files: srv/services-auth.cds, srv/people-service.cds
Check your understanding: Complete labs/lab-04-auth/.
Milestone 5 — Testing by Layer
Goal: Understand what to test at each layer and how to write effective CAP tests.
Tasks:
Run the test suite:
bashnpm run testLook at
test/model.test.js:- Which tests verify model constraints (
@assert.range,@assert.unique)? - Which tests verify service contract (HTTP 200, OData
valuearray)?
- Which tests verify model constraints (
Look at
test/handler.test.js:- Which tests verify handler validation (
beforehook)? - Which tests verify that the
renameaction works correctly? - Which tests verify
after-READ enrichment (displayTitle)?
- Which tests verify handler validation (
Write a new test that verifies
countByGenderreturns0for an unknown gender.
Key files: test/model.test.js, test/handler.test.js
Reference: test/convertData.test.js shows how to test data migration logic separately.