04 · CAP Project Structure

Deep dive into db/, srv/, app/ directories — what belongs where and why. Package.json, .cdsrc.json, and profile configuration.

Lesson 4 — Understanding CAP Project Structure

What you'll learn

Where every file in a CAP project belongs, what .cdsrc.json controls, how profiles work, and how to structure a project that stays clean as it scales to dozens of services and entities.


The full structure at a glance

my-project/
│
├── app/                      ← Fiori / UI5 frontends
│   └── my-app/               ← One subfolder per UI app
│       └── webapp/
│
├── db/                       ← Data model layer
│   ├── schema.cds            ← Entity definitions
│   ├── data/                 ← CSV seed data
│   │   └── my.ns-Books.csv
│   └── src/                  ← HANA-specific artifacts (deployed to HANA only)
│       └── my-view.hdbview
│
├── srv/                      ← Service layer
│   ├── catalog-service.cds   ← Service definition
│   ├── catalog-service.js    ← Custom handlers (same base name)
│   └── lib/                  ← Shared utilities
│
├── .cdsrc.json               ← CAP configuration
├── .env                      ← Local secrets (never commit this)
├── package.json
└── mta.yaml                  ← BTP deployment descriptor (added later)

The db/ folder — your data layer

db/ contains everything related to what you store.

db/
├── schema.cds          ← All entity definitions
├── data/               ← CSV files for local seed data
│   └── ns-Entity.csv   ← Named: <namespace>-<Entity>.csv
└── src/                ← HANA-only artifacts
    └── *.hdbview       ← HANA Calculation Views, Stored Procs

Rules:
- Put all entity definitions in db/schema.cds (or split into multiple .cds files for large projects)
- CSV files in db/data/ are auto-loaded by cds watch into the in-memory SQLite database
- The db/src/ folder is only deployed to HANA, not SQLite — put native HANA SQL here


The srv/ folder — your service layer

srv/ contains everything related to what you expose and how you handle it.

srv/
├── catalog-service.cds     ← OData service definition
├── catalog-service.js      ← Custom handlers (matched by name)
├── admin-service.cds       ← Another service (admin-only)
├── admin-service.js
└── lib/
    └── helpers.js          ← Shared code (not a CDS file)

Critical rule: The .js handler file must have the exact same base name as the .cds service file. CAP auto-connects them:

catalog-service.cds  ←→  catalog-service.js   ✅ Auto-wired
adminService.cds     ←→  adminService.js       ✅ Auto-wired
catalog-service.cds  ←→  handlers.js           ❌ NOT auto-wired

The .cdsrc.json file — CAP configuration

This is the brain of your CAP project. It controls how CAP behaves in different environments.

{
  "requires": {
    "db": {
      "kind": "sqlite",
      "credentials": { "database": ":memory:" }
    }
  },
  "features": {
    "fetch_csrf": true
  }
}

Important settings:

{
  "requires": {
    "db": {
      "[development]": {
        "kind": "sqlite",
        "credentials": { "database": "db.sqlite" }
      },
      "[production]": {
        "kind": "hana"
      }
    },
    "auth": {
      "[development]": { "kind": "mocked" },
      "[production]": { "kind": "xsuaa" }
    }
  }
}

Profiles — switching environments

CAP uses profiles to switch config between dev and production. The profile is set via the NODE_ENV environment variable.

# Development (default)
cds watch                        # Uses [development] profile

# Production
NODE_ENV=production cds serve    # Uses [production] profile

You can define your own profiles:

{
  "requires": {
    "db": {
      "[test]": {
        "kind": "sqlite",
        "credentials": { "database": ":memory:" }
      }
    }
  }
}
NODE_ENV=test npx jest

The package.json — key CAP fields

{
  "name": "my-bookshop",
  "dependencies": {
    "@sap/cds": "^8.0.0",       ← CAP runtime (required)
    "@sap/cds-dk": "^8.0.0",    ← CAP CLI tools (dev)
    "express": "^4.18.0",       ← Web framework
    "@sap/hana-client": "^2.x"  ← HANA driver (add for HANA)
  },
  "scripts": {
    "start": "cds serve --production",
    "watch": "cds watch",
    "build": "cds build/all"
  },
  "cds": {
    "requires": { ... }          ← Can put cdsrc.json content here
  }
}

Tip: For small projects, you can put your CAP config in package.json under the "cds" key instead of a separate .cdsrc.json.


Splitting large schemas

As your project grows, split the data model across multiple files:

db/
├── common.cds          ← Shared types and aspects
├── products.cds        ← Product-related entities
└── orders.cds          ← Order-related entities

In srv/catalog-service.cds, import what you need:

using { sap.capbookshop.Products } from '../db/products';
using { sap.capbookshop.Orders }   from '../db/orders';

Common mistakes

Putting business logic in .cds files
CDS files are schema/definition only. No JavaScript in .cds.

Naming handler file differently from service file
CatalogService.js won't wire to catalog-service.cds. Names must match exactly (with the same casing rules that CDS uses).

Committing .env to git
Always add .env to .gitignore. It contains secrets.


Checkpoint ✓

You now understand:
- [ ] What goes in db/, srv/, and app/
- [ ] How .cdsrc.json profiles switch between SQLite and HANA
- [ ] How handler files are auto-wired to service definitions
- [ ] How to split a large schema into multiple .cds files

In Lesson 5, we go deep into CDS syntax — the language you'll use to define everything.

Sign in for progress tracking & Q&A →