Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member66
Advisor
Advisor
This blog post introduces a new sample repository for a full stack TypeScript App built with the SAP Cloud Application Programming Model for Node.js (CAP) and SAPUI5. It covers a project intended for deployment on the SAP BTP Cloud Foundry environment that uses TypeScript. Find out how such a project is structured, run, built, and deployed!

With all the newly released content around TypeScript for UI5 development, I got curious: Is it time to try TypeScript in a UI project myself? Well, I kind of already gave the answer to this away by now. Still, when I initially started out with the idea, I didn't know how TypeScript and I would get along and what this eventually would turn into. There were lots of lessons to be learned along the way, but in the end, TypeScript won me over. So I created a sample repository for a compact full stack app, where all JavaScript is replaced with TypeScript. 

Let's look into some of the details. I'll outline the project structure and focus on the expanded build process required when using TypeScript.

You can view the entire source code of the sample application on GitHub:
https://github.com/SAP-samples/btp-full-stack-typescript-app

Sample App Overview



Full Stack TypeScript App architecture


The diagram above showcases the different parts the Multitarget Application (MTA) consists of. In addition to the full stack app itself, some SAP BTP services are used, SAP Hana Cloud as database and a simple standalone approuter. All of this lives in the Cloud Foundry environment. A quick summary of what each part does:

The project evolves around a simple lecture schedule scenario. Upon that, the data model consists of Courses, Professors, Rooms, and Lectures. Every lecture has a defined start and end time and is held in one of the rooms. An association to a specific course as well as the lecturing professor is possible. This data is now provided through an OData V4 service built with CAP for Node.js. A dedicated service handler implements a custom action, provides validation when creating a new entry, and adds a non-persistent virtual property. Access to the service is restricted via two different user roles: viewer and admin. Only users with the admin role can change the data. All viewers can read the entries.


Lecture Service Entity Data Model


The data for the lecture schedule is displayed with a freestyle SAPUI5 app. Here two different planning calendars are used to display the lectures over time. Filtering for a professor is possible. Creating, modifying, and deleting entries are shown as options for users with the admin role. This can be checked due to expanding the standalone approuter with an additional route to provide information about the user currently accessing the app.



Development closer look - Build Process


Now it's time to address how the development with TypeScript differs from regular JavaScript coding. 

TypeScript is a superset of JavaScript developed by Microsoft. Essentially it is JavaScript with syntax for types. The idea is to make JavaScript development more efficient by catching mistakes early with the help of its type system. JavaScript is a loosely typed language, which can be a blessing and a curse at the same time. TypeScript, on the other hand, is optionally strongly typed and therefore helps to catch type errors. What would have resulted in a runtime error with JavaScript can be found beforehand with TypeScript static type checking. Moreover, TypeScript brings future JavaScript to coding by enabling features of ES Next for current JavaScript engines.



Difference between TypeScript and JavaScript


TypeScript files, however, cannot be executed. They need to be compiled back to JavaScript before running the code. The build process can be integrated with common build tools, and a linter adds even more options for code checking. Code linting ties right into one of my favorite TypeScript features - enhanced editor integration like code completion.

Some adjustments to the build parameters related to TypeScript are required to build an MTA for Cloud Foundry deployment. The build process needs to include a TypeScript compilation step since the generated JavaScript will be run on Cloud Foundry in the end. Regardless, the actual mta.yaml file does not differ from a regular MTA project. All that changes is the content of the already included build scripts.

CAP Node.js Service using TypeScript


There are only a few things to note when using TypeScript with CAP. First, a tsconfig.json file at the project's root is needed to define the compiler options. A standard Node.js configuration is sufficient. This file also declares which TypeScript sources to include (everything in /srv here) and where to put the compiled files. Now you are ready to start writing TypeScript instead of JavaScript. The type declarations conveniently come with the @sap/cds package, so no additional installs are needed. One of TypeScript's great features is the enhanced editor integration, enabling code assistance with type declarations. This is not limited to pure TypeScript development - they can also enable IntelliSense for JavaScript in an editor like Visual Studio Code. 


Code assistance enabled through @Sap/cds type definitions


For the limited scope of this sample application, the interfaces to make the CDS model available in TypeScript are manually created. There are community tools to generate types based on CDS definitions to facilitate the process. You can look at this blog post to find out more: Taking CAP to the next level – with TypeScript

Things get more challenging when it comes to the familiar watchrun and build commands. The sample project includes several scripts for TypeScript adjusted commands:
 "start-service:ts": "cds-ts run --with-mocks --in-memory",
"watch-service:ts": "cds-ts watch --open",
"build:cf": "npm run build:cds && npm run cleanup:ts && npm run build:ts",
"build:ts": "tsc",
"build:cds": "cds build --production",
"cleanup:ts": "npx rimraf gen/srv/srv/**/*.ts",
...

For development, cds-ts is available to spare us from compiling TypeScript to JavaScript every time we want to run the app. The command utilizes ts-node instead of the regular node engine. This is also applicable to running in watch mode.

However, this should not be used for production due to performance reasons. Therefore TypeScript code needs to be compiled when building the app for production. The build process now includes a regular cds build, removing the unnecessary TypeScript files from the output and compiling all TypeScript to JavaScript files, which are then added to the build result instead.

SAPUI5 Freestyle App using TypeScript


For UI5 development with TypeScript, a variety of valuable resources is already available and gradually expanded:

Generating a new project with the help of the Yeoman Easy UI5 Generator, using the ui5-typescript-helloworld app as a template, or following the step-by-step guide are all great ways to achieve a setup and structure similar to the new Full Stack TypeScript App sample project.

In this approach, Babel is used for compiling TypeScript code to modern ES6 JavaScript and ultimately to classic UI5 code. For this purpose, the project utilized the Babel plugin transform UI5 from Ryan Murphy and a .babelrc.json file for configuration. This comes with the advantage of enabling modern JavaScript features like classes, modules, promises, and async/await for the UI5 coding. 

Therefore, the final build process includes this transpile step with Babel creating a webapp folder with regular UI5 JavaScript code and afterward a ui5 build to bundle the code in a /dist folder.


UI5 resources processing flow


The sample app includes several scripts to watch, run and build the SAPUI5 app taking care of TypeScript handling. 
 "build:cf": "npm run build:ts && npm run build:ui5:cf",
"build:ui5:cf": "ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateManifestBundle generateCachebusterInfo",
"build": "npm-run-all build:ts build:ui5",
"build:ts": "babel src --out-dir webapp --source-maps inline --extensions \".ts,.js\" --copy-files",
"build:ui5": "ui5 build --clean-dest",
"start": "npm-run-all --parallel watch:ts start:ui5",
"watch:ts": "babel src --out-dir webapp --source-maps inline --extensions \".ts,.js\" --copy-files --watch",
"start:ui5": "ui5 serve --port 8080 -o index.html",
...

 

Round off


Aiming to cover every possible piece of JavaScript, the approuter extension is also implemented in TypeScript. Unfortunately, no type definitions are provided for the @sap/approuter package at the time of writing. Nevertheless, this does not rule out using TypeScript in general since, technically, all correct JavaScript code is naturally valid TypeScript. It only limits the benefit of doing so. This reveals another TypeScript benefit, which comes into play when gradually converting a JavaScript code basis to TypeScript.

Next Steps


Take a look at the sample repository for the Full Stack TypeScript App on GitHub and try out TypeScript for Cloud development yourself!

  1. Clone the project from GitHub:
    git clone https://github.com/SAP-samples/btp-full-stack-typescript-app.git
    cd btp-full-stack-typescript-app


  2. Install the dependencies:
    npm install


  3. Run the app locally:
    npm run start-service:ts


10 Comments
former_member194549
Contributor
Hi Lena,

very interesting blog.
Regarding CAP & TypeScript I can recommend my blog. This one also describes how to use cds2types. So you don't have to write the interfaces for the entities yourself, they can be compiled from the CDS definitions.

Regards
Simon
former_member66
Advisor
Advisor
Hi Simon,

Thanks for commenting and pointing to your blog post again. A link to it is also included in the CAP TypeScript section of this post, where I talk about interfaces.

Regards,
Lena
hschaefer123
Participant
Hi Lena,

very nice sum up and i saw you also added content to the FullStack TS Repo.

Just one idea concerning AppRouter.

I personally follow the CAP guide concerning TS recommendations -> for productive usage always precompile TS.

You are using ts-node and typescript as dependency while CAP just uses it as a devDependency.

I personally would always precompile code before deploying and trying to use as less as possible dependencies inside the productive package. In the past, there has been a lot of scenarios where you even had to decide including node_modules into mta package or not for multiple issue reasons like sqlite throwing build error.

The comfort of using ts-node and CAP cds-ts watch is greate during development, but i would be to afraid, introducing additional runtime layers like ts-node that posibly produce errors while deploying and staging BTP services.

Even for costs reason (1 GB BTP runtime = ~70,- Euro/month), i always try to strip down services as much as possible (memory and disk quota).

But that is only my opinion. Maybe today or in the future, ts-node will be the new standard or node will support it out-of-the-box 😉

Looking forward for next posts of you!

Regards
Holger
gregorw
Active Contributor
Hi Holger,

I fully agree.

You're right with the costs for the BTP CF runtime. But it's good that the disk quota doesn't count there.

CU
Gregor
former_member66
Advisor
Advisor

Hi Holger,

I am with you on that take - precompiling is the way to go for TS. The sample repo has been updated with some adjustments to the AppRouter to reflect that earlier this week.

Regards
Lena

namas1994
Advisor
Advisor
0 Kudos
Hi gregorw

I have cloned the repo: SAP-samples / btp-full-stack-typescript-app to check how I can enable my CAP service with Typescript.
When I try to run the service, it is starting successfully but I am getting the following error, when I open the preview app of any entity: Lectures/Rooms/Courses/Professor.

"App could not be opened because the SAP UI5 component of the application could not be loaded.
Failed to load UI5 component for navigation intent "#preview-app" "

Any I idea how to resolve this?

Thanks in advance.

Namasivayam
jasonmuzzy
Active Participant
0 Kudos
I noticed that the Full Stack TypeScript App on GitHub uses srv.read in the service.ts file.  Is there an example of how to use the CQL constructs like SELECT.columns().from().where() as mentioned in the changelogs?  Currently when I use SELECT in a .ts file I get an error stating "Cannot find name 'SELECT'".

 

 
merveguel
Participant
0 Kudos

Hi Lena,

 

Thanks for this informative blog post.

I wanted to try a cloud foundry deployable basic typescript app from scratch with the help of your shared repo. I was able to successfully build a MTA project with a basic Typescript app and deploy it  to BTP, but for some reason Component.js is not get recognized by the BTP Launchpad service. Would you be able to kindly make a comment onto that? Is there any special build/deployment command for Typescript applications different from CAP application deployment that we suppose to use before/during deployment?

 

Thanks in advance,

Merve

failed to load helloworld/Componentjs

 

MTA.yaml

_schema-version: "3.2"
ID: ui5-ts-approuter
description: Fiori elements app
version: 0.0.1
modules:
- name: ui5-ts-approuter-dest-content
type: com.sap.application.content
requires:
- name: ui5-ts-approuter-destination-service
parameters:
content-target: true
- name: ui5-ts-approuter-repo-host
parameters:
service-key:
name: ui5-ts-approuter-repo-host-key
- name: ui5-ts-approuter-uaa
parameters:
service-key:
name: ui5-ts-approuter-uaa-key
parameters:
content:
instance:
destinations:
- Name: ui5-ts-approuter_repo_host
ServiceInstanceName: ui5-ts-approuter-html5-srv
ServiceKeyName: ui5-ts-approuter-repo-host-key
sap.cloud.service: ui5-ts-approuter
- Authentication: OAuth2UserTokenExchange
Name: ui5-ts-approuter_uaa
ServiceInstanceName: ui5-ts-approuter-xsuaa-srv
ServiceKeyName: ui5-ts-approuter-uaa-key
sap.cloud.service: ui5-ts-approuter
existing_destinations_policy: update
build-parameters:
no-source: true
- name: ui5-ts-approuter-app-content
type: com.sap.application.content
path: .
requires:
- name: ui5-ts-approuter-repo-host
parameters:
content-target: true
build-parameters:
build-result: resources
requires:
- artifacts:
- ui5tsapprouter.zip
name: ui5tsapprouter
target-path: resources/
- name: ui5tsapprouter
type: html5
path: app/helloworld
build-parameters:
build-result: dist
builder: custom
commands:
- npm install
- npm run build:cf
supported-platforms: []
resources:
- name: ui5-ts-approuter-uaa
type: org.cloudfoundry.managed-service
parameters:
path: ./xs-security.json
service: xsuaa
service-name: ui5-ts-approuter-xsuaa-srv
service-plan: application
- name: ui5-ts-approuter-destination-service
type: org.cloudfoundry.managed-service
parameters:
config:
HTML5Runtime_enabled: true
version: 1.0.0
init_data:
instance:
existing_destinations_policy: update
destinations:
- Name: ui5
Type: HTTP
URL: https://ui5.sap.com
ProxyType: Internet
Authentication: NoAuthentication
service: destination
service-name: ui5-ts-approuter-destination-service
service-plan: lite
- name: ui5-ts-approuter-repo-host
type: org.cloudfoundry.managed-service
parameters:
service: html5-apps-repo
service-name: ui5-ts-approuter-html5-srv
service-plan: app-host
parameters:
deploy_mode: html5-repo

 

app/helloworld folder package.json

{
"name": "src",
"version": "1.0.0",
"description": "UI5 Application: src",
"author": "merveguel",
"license": "Apache-2.0",
"scripts": {
"build:cf": "npm run build:ts && npm run build:ui5:cf",
"build:ts": "babel src --out-dir webapp --source-maps inline --extensions \".ts,.js\" --copy-files",
"build:ui5:cf": "ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateManifestBundle generateCachebusterInfo",
"build": "rimraf resources mta_archives && mbt build --mtar archive",
"build:opt": "ui5 build self-contained --clean-dest --all",
"start": "ui5 serve --port 8080 -o index.html",
"start:dist": "ui5 serve --port 8080 -o index.html --config ui5-dist.yaml",
"ts-typecheck": "tsc --noEmit",
"lint": "eslint webapp",
"test": "npm run lint",
"undeploy": "cf undeploy undefined --delete-services --delete-service-keys --delete-service-brokers",
"deploy": "cf deploy mta_archives/archive.mtar --retries 1"
},
"devDependencies": {
"@babel/cli": "^7.21.5",
"@babel/core": "^7.20.12",
"@babel/plugin-transform-typescript": "^7.21.3",
"@babel/preset-env": "^7.21.5",
"@babel/preset-typescript": "^7.21.5",
"@sap/ui5-builder-webide-extension": "^1.1.9",
"@sap/ux-ui5-tooling": "^1.9.6",
"@sapui5/types": "1.114.0",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@ui5/cli": "^3.1.2",
"babel-preset-transform-ui5": "^7.1.4",
"eslint": "^8.37.0",
"i": "^0.3.7",
"mbt": "^1.2.23",
"npm": "^9.6.7",
"rimraf": "^3.0.2",
"typescript": "^5.0.3",
"ui5-middleware-livereload": "^0.8.2",
"ui5-task-zipper": "^0.8.2",
"ui5-tooling-transpile": "^0.5.2"
}
}
Dragolea
Participant
0 Kudos

Hi Lena,

Maybe you can have a look over the below npm packages, which might be helpful on SAP CAP TS projects

  • CDS-TS-Dispatcher - TypeScript package for handling the events, support for draft
    • Example draft decorators :
      • @OnReadDraft(), @AfterReadDraft(), @OnSaveDraft()
    • Example active entity methods
      • @OnRead(), @AfterRead(), @BeforeRead()
      • and many more ...
  • CDS-TS-Repository - Simplified interface for common database actions
    • Example .create, createMany, update, delete, deleteMany, exists, getLocaleTexts, count, updateLocaleTexts ... and many more 

Examples how to use the CDS-TS-Dispatcher & CDS-TS-Repository => GitHub

nils_sap
Explorer
0 Kudos
As this excellent blog post is already 2 years old, I would recommend to have a look at the sap ui5 typescript tutorial instead. GitHub - SAP-samples/ui5-typescript-tutorial: Tutorial for building UI5 applications with TypeScript...

That one is regularly maintained and there is a very good video attached to it.