Skip to content

    Getting Started in a Nutshell

    Using a minimalistic setup

    Get started in a minimalistic setup with Node.js, express and SQLite.
    See also: Java-based variant of this guide.

    This guide is also available as CAP Notebook.

    You’ll learn, step by step, how to do the following:

    Jumpstarting Projects

    After installing @sap/cds-dk globally, create a project with minimal defaults as follows:

    cds init bookshop

    To copy code snippets, click the icon in the upper-right corner of the code box.

    Download from cap/samples (Optional)

    Instead of going for a manual step-by-step experience, you can also get the cap/samples from GitHub:

    git clone samples
    cd samples
    npm install

    Go to the bookshop folder

    In the following, we assume that you’re in the bookshop folder unless told otherwise.

    cd bookshop

    Launch cds watch

    For an automated jumpstart, you can just tell cds to watch out for things to arrive:

    cds watch

    Use cds watch to start a cds server, even in a newly created and yet empty project. Whenever you feed your project with new content, for example, by adding or modifying .cds, .json, or .js files, the server automatically restarts to serve the new content. Because there isn’t any content in your project yet, it just keeps waiting with a message like this:

    [cds] - running nodemon...
    --ext cds,csn,csv,ts,mjs,cjs,js,json,properties,edmx,xml
        No models found at db/,srv/,app/,schema,services,.
        Waiting for some to arrive...

    Defining Domain Models

    Let’s feed our project by adding a simple domain model. Start by creating a file named db/schema.cds (also indicated in the code box’s label) and copy the following definitions into it:


    using { Currency, managed, sap } from '@sap/cds/common';
    namespace sap.capire.bookshop;
    entity Books : managed {
      key ID : Integer;
      title  : localized String(111);
      descr  : localized String(1111);
      author : Association to Authors;
      genre  : Association to Genres;
      stock  : Integer;
      price  : Decimal(9,2);
      currency : Currency;
    entity Authors : managed {
      key ID : Integer;
      name   : String(111);
      books  : Association to many Books on = $self;
    /** Hierarchically organized Code List for Genres */
    entity Genres : sap.common.CodeList {
      key ID   : Integer;
      parent   : Association to Genres;
      children : Composition of many Genres on children.parent = $self;

    Find this source also in cap/samples. Learn more about Domain Modeling. Learn more about CDS Modeling Languages.

    Deployed to Databases Automatically

    As soon as you save your file, the still running cds watch reacts immediately with new output like this:

    [cds] - connect to db { database: ':memory:' }
    /> successfully deployed to sqlite in-memory db

    This means that cds watch detected the changes in db/schema.cds and automatically bootstrapped an in-memory SQLite database when restarting the server process.

    Learn more about using databases.

    Compiling Models (Optional)

    We can also test-compile models individually to check for validity and produce a parsed output in CSN format. For example, run this command in a new terminal:

    cds db/schema.cds

    This dumps the compiled CSN model as a plain JavaScript object to stdout.
    Add --to <target> (shortcut -2) to produce other outputs, for example:

    cds db/schema.cds -2 json
    cds db/schema.cds -2 yml
    cds db/schema.cds -2 sql

    Learn more about the command line interface by executing cds --help.

    Defining Services

    After the recent changes, cds watch also prints this message:

        No service definitions found in loaded models.
        Waiting for some to be added...

    So, let’s go on feeding it with service definitions. Following the best practice of single-purposed services, we’ll define two services for different use cases.

    One for Admins to Maintain Books and Authors


    using { sap.capire.bookshop as my } from '../db/schema';
    service AdminService @(requires:'authenticated-user') {
      entity Books as projection on my.Books;
      entity Authors as projection on my.Authors;

    Find this source also in cap/samples.

    And One for End Users to Browse and Order Books


    using { sap.capire.bookshop as my } from '../db/schema';
    service CatalogService @(path:'/browse') {
      @readonly entity Books as SELECT from my.Books {*, as author
      } excluding { createdBy, modifiedBy };
      @requires: 'authenticated-user'
      action submitOrder (book: Books:ID, quantity: Integer);

    Find this source also in cap/samples. Learn more about Defining Services.

    Served to OData out-of-the-box

    This time cds watch reacted with additional output like this:

    [cds] - serving AdminService { at: '/admin' }
    [cds] - serving CatalogService { at: '/browse', impl: 'bookshop/srv/cat-service.js' }
    [cds] - launched in: 744.291ms
    [cds] - server listening on { url: 'http://localhost:4004' }

    As you can see in the log output, the two service definitions have been compiled and generic service providers have been constructed to serve requests on the listed endpoints /admin and /browse.

    Open http://localhost:4004 in your browser and see the generic index.html page: Generic welcome page generated by CAP that list all endpoints. Eases jumpstarting development and is not meant for productive use.

    User alice is a default user with admin privileges. Use it to access the admin service. You don’t need to enter a password.

    Compiling APIs (Optional)

    You can also compile service definitions explicitly, for example to an OData model:

    cds srv/cat-service.cds -2 edmx

    Essentially, using a CLI, this invokes what happened automatically behind the scenes in the previous steps. While we don’t really need such explicit compile steps, you can do this to test correctness on the model level, for example.

    Using Databases

    Using sqlite In-Memory Database

    As previously shown, cds watch automatically bootstraps an SQLite in-process and in-memory database by default — that is, unless told otherwise. While this isn’t meant for productive use, it drastically speeds up development turn-around times, essentially by mocking your target database, for example, SAP HANA.

    Learn more about mocking options in Grow as you go.

    Adding Initial Data in .csv Files

    Now, let’s fill your database with initial data by adding a few plain CSV files under db/data like this:


    201;Wuthering Heights;101;12
    207;Jane Eyre;107;11
    251;The Raven;150;333


    101;Emily Brontë
    107;Charlotte Brontë
    150;Edgar Allen Poe
    170;Richard Carpenter

    Find a full set of .csv files in cap/samples.

    After you’ve added these files, cds watch restarts the server with output, telling us that the files have been detected and their content been loaded into the database automatically:

    [cds] - connect to db { database: ':memory:' }
     > filling sap.capire.bookshop.Authors from bookshop/db/data/sap.capire.bookshop-Authors.csv
     > filling sap.capire.bookshop.Books from bookshop/db/data/sap.capire.bookshop-Books.csv
     > filling sap.capire.bookshop.Books_texts from bookshop/db/data/sap.capire.bookshop-Books_texts.csv
     > filling sap.capire.bookshop.Genres from bookshop/db/data/sap.capire.bookshop-Genres.csv
     > filling sap.common.Currencies from common/data/sap.common-Currencies.csv
     > filling sap.common.Currencies_texts from common/data/sap.common-Currencies_texts.csv
    /> successfully deployed to sqlite in-memory db

    This is the output when you’re using the samples. It’s less if you’ve followed the manual steps here.

    Learn more about Using Databases.

    Querying Through OData out-of-the-box

    Now that we’ve a connected, fully capable SQL database, filled with some initial data, we can send complex OData queries, served by the built-in generic providers:

    Use Alice as user to query the admin service. You don’t need to enter a password.

    Learn more about Generic Providers. Learn more about OData’s Query Options.

    Deploying Persistent Databases

    Instead of using in-memory, we can also use persistent databases. For example, still with SQLite:

    npm add sqlite3 -D
    cds deploy --to sqlite:my.db

    The difference from the automatically provided in-memory database is that we now get a persistent database stored in the local file ./my.db. This is also recorded in the package.json.

    To see what that did, use the sqlite3 CLI with the newly created database:

    sqlite3 my.db .dump
    sqlite3 my.db .tables

    You could also deploy to a provisioned SAP HANA database using this variant:

    cds deploy --to hana

    Learn more about deploying to SAP HANA.

    Adding/Serving UIs

    You can consume the provided services, for example, from UI frontends, using standard AJAX requests. Simply add an index.html file into the app/ folder, to replace the generic index page.

    Vue.js UIs

    For example, you can find a simple Vue.js app in cap/samples, which demonstrates browsing and ordering books using OData requests to the CatalogService API we defined above.

    Shows the famous bookshop catalog service in a simple Vue.js UI.

    SAP Fiori UIs

    Besides, being usable from any UI frontends using standard AJAX requests, CAP provides out-of-the-box support for SAP Fiori UIs, for example, with respect to SAP Fiori annotations and advanced features such as search, value helps and SAP Fiori draft.

    Shows the famous bookshop catalog service in an SAP Fiori UI.

    Learn more about Serving Fiori UIS.

    Using OData Protocol

    As CAP-based services are full-fledged OData services out-of-the-box, you can use advanced query options, such as $select, $expand, $search, and many more.

    Learn more about Serving OData Protocol.

    Adding Custom Logic

    While the generic providers serve most CRUD requests out-of-the-box, you can add custom code to deal with the specific domain logic of your application.

    Providing Service Implementations

    In Node.js, the easiest way to provide implementations for services is through equally named .js files placed next to a service definition’s .cds file:

      - cat-service.cds  # service definitions
      - cat-service.js   # service implementation

    See these files also in cap/samples/bookshop/srv folder. Learn more about providing service implementations in Node.js. Learn also how to do that in Java using Event Handler Classes.

    Adding Custom Event Handlers

    Service implementations essentially consist of one or more event handlers. Copy this into srv/cat-service.js to add custom event handlers:

    const cds = require('@sap/cds')
    module.exports = function (){
      // Register your event handlers in here, for example, ...
      this.after ('READ','Books', each => {
        if (each.stock > 111) {
          each.title += ` -- 11% discount!`

    Learn more about adding event handlers using <srv>.on/before/after. Learn also how to do that in Java using Event Handler Methods.

    Consuming Other Services

    Quite frequently, event handler implementations consume other services, sending requests and queries, as in the completed example below.


    const cds = require('@sap/cds')
    module.exports = async function (){
      const db = await'db') // connect to database service
      const { Books } = db.entities         // get reflected definitions
      // Reduce stock of ordered books if available stock suffices
      this.on ('submitOrder', async req => {
        const {book,quantity} =
        const n = await UPDATE (Books, book)
          .with ({ stock: {'-=': quantity }})
          .where ({ stock: {'>=': quantity }})
        n > 0 || req.error (409,`${quantity} exceeds stock for book #${book}`)
      // Add some discount for overstocked books
      this.after ('READ','Books', each => {
        if (each.stock > 111)  each.title += ` -- 11% discount!`

    Find this source also in cap/samples. Learn more about connecting to services using cds.connect. Learn more about reading and writing data using cds.ql. Learn more about using reflection APIs using <srv>.entities. Learn also how to do that in Java using @Autowired,, etc.

    Test this implementation, for example using the Vue.js app, and see how discounts are displayed in some book titles. Or submit orders until you see the error messages.

    Summary and Next Steps

    With this getting started guide we introduced many of the basics of CAP, such as:

    Visit our Cookbook to find more task-oriented guides. For example, you can find guides about potential next steps such as adding Authentication and Authorization or Deploying to SAP BTP, Cloud Foundry environment.

    Also see the reference sections to find detailed documentation about CDS, as well as Node.js and Java Service SDKs and runtimes.