# Create a Business Service with Node.js Using Visual Studio Code
Develop a sample business service using Core Data & Services (CDS), Node.js, and SQLite, by using the SAP Cloud Application Programming Model (CAP) and developing on your local environment.
**You will learn**
* How to develop a sample business service using CAP and Node.js
* How to define a simple data model and a service that exposes the entities you created in your data model
* How to run your service locally
* How to deploy the data model to an SQLite database
* How to add custom handlers to serve requests that aren’t handled automatically
* How to deploy your business service on SAP Cloud Platform using the SAP Cloud Application Programming Model (CAP) and binding appropriate service instances (see the Developer Guide for Cloud Foundry).
**Prerequisites**
* You’ve installed [Node.js](https://nodejs.org/en/) version 8.9 or higher.
* You’ve installed the latest version of [Visual Studio Code](https://code.visualstudio.com/).
* (For Windows users only) You’ve installed the [SQLite tools](https://sqlite.org/download.html) for Windows.
* You’ve installed [Postman application](https://www.getpostman.com) or any other HTTP client.
* If you don’t have a Cloud Foundry Trial Subaccount on [SAP Cloud Platform](https://cockpit.hanatrial.ondemand.com/cockpit/#/home/trial) yet, create your [Cloud Foundry Trial Account](https://developers.sap.com/tutorials/hcp-create-trial-account.html).
* You’ve downloaded and installed the [cf command-line client](https://github.com/cloudfoundry/cli#downloads) for Cloud Foundry.
**Step 1: Start project**
1. Open a command-line window and run the following command in a folder of your choice:
`cds init my-bookshop`
This creates a folder my-bookshop in the current directory.
2. Open Visual Studio Code, go to File | Open Folder and choose the my-bookshop folder.

3. Go to Terminal | New Terminal to open a command-line window within Visual Studio Code and run the following command:
`cds watch`
This command tries to start a cds server process.
As there’s no content in the project so far, it just keeps waiting for content with a message as shown below:
```
[cds] - running nodemon...
--exec cds run --with-mocks --in-memory?
--ext cds,csn,csv,ts,mjs,cjs,js,json,edmx,xml
No models found at ./db ./srv ./schema.cds ./services.cds.
Waiting for some to arrive...
```
**Step 2: Define your first service**
Create a simplistic all-in-one service definition.
1. In Visual Studio Code, choose the New File icon and type srv/cat-service.cds.

This creates a folder called srv and a file called cat-service.cds.
2. Open the file and add the following code:
```
using { Country, managed } from '@sap/cds/common';
service CatalogService {
entity Books {
key ID : Integer;
title : localized String;
author : Association to Authors;
stock : Integer;
}
entity Authors {
key ID : Integer;
name : String;
books : Association to many Books on books.author = $self;
}
entity Orders : managed {
key ID : UUID;
book : Association to Books;
country : Country;
amount : Integer;
}
}
```
3. Save your file.
4. As soon as you’ve saved your file, the running cds watch reacts immediately with an output as shown below:
```
[cds] - connect to datasource - sqlite::memory:
/> successfully deployed to sqlite in-memory db
[cds] - serving CatalogService at /catalog
[cds] - launched in: 977.671ms
[cds] - server listening on http://localhost:4004 ...
```
5. To test your service, go to: http://localhost:4004
You won’t see data, because you haven’t added a data model yet. However, click on the available links to see the service is running.
**Step 3: Provide mock data**
Add service provider logic to return mock data.
1. In the srv folder, create a new file called cat-service.js.
2. Add the following javascript code:
```
```
module.exports = (srv) => {
// Reply mock data for Books...
srv.on ('READ', 'Books', ()=>[
{ ID:201, title:'Wuthering Heights', author_ID:101, stock:12 },
{ ID:251, title:'The Raven', author_ID:150, stock:333 },
{ ID:252, title:'Eleonora', author_ID:150, stock:555 },
{ ID:271, title:'Catweazle', author_ID:170, stock:222 },
])
// Reply mock data for Authors...
srv.on ('READ', 'Authors', ()=>[
{ ID:101, name:'Emily Brontë' },
{ ID:150, name:'Edgar Allen Poe' },
{ ID:170, name:'Richard Carpenter' },
])
}
```
```
3. Save the file.
4. To test your service, click on these links:
http://localhost:4004/catalog/Books
http://localhost:4004/catalog/Authors
You should see the mock data that you added for the Books and Authors entities.
**Step 4: Add data model and adapt service definition**
To get started quickly, you’ve already added a simplistic all-in-one service definition. However, you would usually put normalized entity definitions into a separate data model and have your services expose potentially de-normalized views on those entities.
1. Choose New File and type db/data-model.cds.

This creates a folder called db and a file called data-model.cds. Your project structure should look like this:

2. Add the following code to the data-model.cds file:
```
namespace my.bookshop;
using { Country, managed } from '@sap/cds/common';
entity Books {
key ID : Integer;
title : localized String;
author : Association to Authors;
stock : Integer;
}
entity Authors {
key ID : Integer;
name : String;
books : Association to many Books on books.author = $self;
}
entity Orders : managed {
key ID : UUID;
book : Association to Books;
country : Country;
amount : Integer;
}
```
3. Open cat-service.cds and replace the code with:
```
using my.bookshop as my from '../db/data-model';
service CatalogService {
entity Books @readonly as projection on my.Books;
entity Authors @readonly as projection on my.Authors;
entity Orders @insertonly as projection on my.Orders;
}
```
Remember to save your files.
**Step 5: Add initial data**
Add plain CSV files under db/csv to fill your database tables with initial data.
1. In the db folder, choose New File and enter csv/my.bookshop-Authors.csv. Add the following to the file:
```
ID;name
101;Emily Brontë
107;Charlote Brontë
150;Edgar Allen Poe
170;Richard Carpenter
```
2. In the db folder, choose New File and enter csv/my.bookshop-Books.csv. Add the following to the file:
```
ID;title;author_ID;stock
201;Wuthering Heights;101;12
207;Jane Eyre;107;11
251;The Raven;150;333
252;Eleonora;150;555
271;Catweazle;170;22
```
Make sure that you now have a folder hierarchy db/csv/.... Remember that the csv files must be named like the entities in your data model and must be located inside the db/csv folder.
As we now have a fully capable SQL database with some initial data, we can send complex OData queries, served by the built-in generic providers.
3. Remove the code with mock data in cat-service.js, because we want to see the data loaded from the csv files.
4. To test your service, open a web browser and go to:
http://localhost:4004/catalog/Books
http://localhost:4004/catalog/Authors
As we now have a fully capable SQL database with some initial data, we can send complex OData queries, served by the built-in generic providers.
http://localhost:4004/catalog/Authors?$expand=books($select=ID,title)
You should see a book titled Jane Eyre. If not, make sure you’ve removed the mock data from cat- service.js.
**Step 6: Add persistent database**
Instead of using in-memory, we can also use persistent databases.
1. If cds watch is running, press CTRL+C to stop the service.
2. Install SQLite3 packages.
`npm i sqlite3 -D`
3. Deploy the data model to an SQLite database:
`cds deploy --to sqlite:db/my-bookshop.db`
You’ve now created an SQLite database file under db/my-bookshop.db.
This configuration is saved in your package.json as your default data source. For subsequent deployments using the default configuration, you just need to run cds deploy.The difference to the automatically provided in-memory db is that we now get a persistent database stored in the local file.
4. Open SQLite and view the newly created database:
`sqlite3 db/my-bookshop.db -cmd .dump`
If this doesn’t work, check if you have SQLite installed. On Windows, you might need to enter the full path to SQLite, for example: C:\sqlite\sqlite3 db/my-bookshop.db -cmd .dump.
5. To stop SQLite and go back to your project directory, press CTRL+C.
6. Run your service.
`cds watch`
```
[cds] - connect to datasource - sqlite:db/my-bookshop.db
[cds] - serving CatalogService at /catalog - with impl: srv/cat-service.js
[cds] - launched in: 4356.854ms
[cds] - server listening on (http://localhost:4004) ...
```
**Step 7: Test generic handlers with Postman**
We can now see the generic handlers shipped with CAP in action.
1. Open the Postman application.
You can use any other HTTP client than Postman.
2. Click on the following link and save the file to a folder of your choice: postman.json.
3. In the Postman app, use the Import button in the toolbar:

4. Choose Import from File in the wizard. Click on Choose Files and select the file that you saved before.

5. In the imported collection, execute the various requests in the metadata and CRUD groups. They should all return proper responses

With our current service implementation, we can get only POST orders. Any GET or DELETE to an order fails, since we’ve specified the Orders entity to be @insertonly in srv/cat-service.cds.
**Step 8: Add custom logic**
1. Add the following code in the srv/cat-service.js file:
```
```
module.exports = (srv) => {
const {Books} = cds.entities ('my.bookshop')
// Reduce stock of ordered books
srv.before ('CREATE', 'Orders', async (req) => {
const order = req.data
if (!order.amount || order.amount <= 0) return req.error (400, 'Order at least 1 book')
const tx = cds.transaction(req)
const affectedRows = await tx.run (
UPDATE (Books)
.set ({ stock: {'-=': order.amount}})
.where ({ stock: {'>=': order.amount},/*and*/ ID: order.book_ID})
)
if (affectedRows === 0) req.error (409, "Sold out, sorry")
})
// Add some discount for overstocked books
srv.after ('READ', 'Books', each => {
if (each.stock > 111) each.title += ' -- 11% discount!'
})
}
```
```
Whenever orders are created, this code is triggered. It updates the book stock by the given amount, unless there aren’t enough books left.
2. In Postman, execute the GET Books request.
Look at the stock of book 201.

3. Execute one of the POST Orders requests.
This triggers the logic above and reduce the stock.
4. Execute the GET Books request again.
The stock of book 201 is lower than before.
**Step 9: Enhance project configuration for SAP HANA**
It’s now time to switch to SAP HANA as a database.
1. Add the following configuration in the package.json file of your my-bookshop project (overwrite any existing cds configuration):
```
"cds":{
"requires": {
"db": {
"kind": "hana",
"model": ["db","srv"]
}
}
}
```
2. Add the SAP HANA driver as a dependency to your project:
`npm add hdb`
If your .cdsrc.json file contains a "target" entry, remove it or set it to: "target": "gen". This causes deployment files to be written to this folder. Otherwise, the deployment files would be written to the source folders.
**Step 10: Deploy using cf push**
Cloud Foundry environment of SAP Cloud Platform has a built-in cf push command to deploy applications. It needs the application files plus an optional manifest.yml file to push the application code and to bind the relevant services to the application.
1. As cf push can only bind but not create services, you need to create the SAP HANA service manually (along with an HDI container and a database schema):
`cf create-service hanatrial hdi-shared my-bookshop-db-hdi-container`
This process takes some minutes.
Check the status of your service using cf service my-bookshop-db-hdi-container.
If service creation fails, see the Troubleshooting guide for CAP.
2. Now, build and deploy both the database part and the actual application:
```
cds build/all
cf push -f gen/db
cf push -f gen/srv --random-route
```
This process takes some minutes.
The first command creates the SAP HANA table and view definitions along with manifest.yaml files in both in gen/db and gen/srv folders. Look at gen/db/manifest.yaml and see that it binds to the my- bookshop-db-hdi-container service that you’ve created in the previous step.
3. In the deploy log, find the application URL in the routes line at the end:
```
name: my-bookshop-srv
requested state: started
routes: my-bookshop-srv-....cfapps.sap.hana.ondemand.com
```
4. Open this URL in the browser and try out the provided links, for example, .../catalog/Books. Application data is fetched from SAP HANA.
*Congratulations for running your first CAP based application on SAP Cloud Platform.*