03 · Your First CAP Project

Scaffold a CAP project with cds init, explore the generated folder structure, and start the dev server with cds watch.

Lesson 3 — Your First CAP Project

What you'll build

A running CAP project with a bookshop data model and service — with live OData endpoints accessible in your browser, all in under 10 minutes.


Step 1 — Create the project

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

Step 2 — Understand the generated structure

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


Step 3 — Add a data model

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


Step 4 — Define a service

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


Step 5 — Start the server

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.


Step 6 — Explore the auto-generated API

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.


Step 7 — Seed initial data

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.


Step 8 — Try OData queries

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.


Common mistakes

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.


Checkpoint ✓

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 →