Scaffold a CAP project with cds init, explore the generated folder structure, and start the dev server with cds watch.
A running CAP project with a bookshop data model and service — with live OData endpoints accessible in your browser, all in under 10 minutes.
Open a terminal in an empty folder and run:
mkdir my-bookshop && cd my-bookshop
cds init
You'll see:
Creating new CAP project in ./my-bookshop
Adding .gitignore
Adding package.json
Adding .cdsrc.json
Adding README.md
Now install dependencies:
npm install
my-bookshop/
├── app/ ← Fiori UI apps go here (empty for now)
├── db/ ← Data model (.cds files)
├── srv/ ← Service definitions and custom handlers
├── .cdsrc.json ← CAP configuration
├── package.json
└── README.md
Rule of thumb:
db/= what you store,srv/= what you expose
Create db/schema.cds:
namespace my.bookshop;
entity Books {
key ID : UUID @Core.Computed;
title : String(100) not null;
author : String(100);
stock : Integer default 0;
price : Decimal(10, 2);
}
entity Authors {
key ID : UUID @Core.Computed;
name : String(100) not null;
books : Association to many Books on books.author = $self.name;
}
What's happening here:
- namespace — groups your entities to avoid name collisions
- key ID : UUID — auto-generated UUID primary key
- @Core.Computed — tells OData this field is server-managed (client cannot set it)
- Association to many — a relationship to Books
Create srv/catalog-service.cds:
using my.bookshop as db from '../db/schema';
service CatalogService {
entity Books as projection on db.Books;
entity Authors as projection on db.Authors;
}
What's happening:
- using ... from — imports the data model
- as projection on — exposes the entity through the service
- By default, all CRUD operations (CREATE, READ, UPDATE, DELETE) are enabled
cds watch
You'll see output like:
[cds] - model loaded from 2 file(s):
db/schema.cds
srv/catalog-service.cds
[cds] - connect to db > sqlite { database: ':memory:' }
[cds] - serving CatalogService { path: '/odata/v4/catalog' }
[cds] - server listening on { url: 'http://localhost:4004' }
[cds] - launched at ..., version: 8.x.x
Open your browser: http://localhost:4004
You'll see the CAP welcome page with a link to your service.
Click on CatalogService in the browser. You'll see links to:
/odata/v4/catalog/Books ← GET all books
/odata/v4/catalog/Authors ← GET all authors
/odata/v4/catalog/$metadata ← Full OData metadata document
Click on Books — you'll get:
{ "@odata.context": "...", "value": [] }
Empty for now — no data yet. Let's add some.
Create db/data/my.bookshop-Books.csv:
ID,title,author,stock,price
f1f1f1f1-0000-0000-0000-000000000001,The Hitchhiker's Guide,Douglas Adams,100,12.99
f1f1f1f1-0000-0000-0000-000000000002,Clean Code,Robert C. Martin,50,35.00
f1f1f1f1-0000-0000-0000-000000000003,Domain-Driven Design,Eric Evans,30,45.00
File naming convention:
<namespace>-<EntityName>.csv
The cds watch process auto-reloads and picks up the CSV. Refresh /odata/v4/catalog/Books in your browser — you'll see the three books.
In your browser or REST Client, try these:
# Filter books under $20
GET http://localhost:4004/odata/v4/catalog/Books?$filter=price lt 20
# Select only title and price
GET http://localhost:4004/odata/v4/catalog/Books?$select=title,price
# Sort by price descending
GET http://localhost:4004/odata/v4/catalog/Books?$orderby=price desc
# Take only top 2
GET http://localhost:4004/odata/v4/catalog/Books?$top=2
All of these work without writing a single line of handler code. CAP's generic providers handle it automatically.
Error: Cannot find module '@sap/cds'
You forgot to run npm install. Run it now.
CSV data not loading
Check the file name matches exactly: namespace-EntityName.csv. Case sensitive.
Port 4004 already in use
Another CAP server is running. Press Ctrl+C in the other terminal, or run cds watch --port 4005.
You should have:
- [ ] A running CAP server at http://localhost:4004
- [ ] Three books visible at /odata/v4/catalog/Books
- [ ] Tested at least two OData query options ($filter, $select, etc.)
In Lesson 4, we go deeper into the project structure so you know exactly where everything belongs as your app grows.
Sign in for progress tracking & Q&A →