Skip to main content

approuter-setup

Invoke this skill to set up the SAP Application Router (approuter) for Cloud Foundry ONLY for apps that serve static UI (HTML/CSS/JS under webapp/) or require authentication (web.xml <auth-method>/<security-constraint>, or XSUAA). A bare web.xml on an API-only, no-auth backend (a servlet returning JSON/text) does NOT need an approuter — do not invoke this skill for it. Creates the approuter directory with package.json, xs-app.json, and optional extended middleware for SAML2/Basic Auth handling.

Source facts

Repository
SAP-samples/btp-neo-java-app-migration
Last source activity
July 29, 2026 at 13:26
Detected SKILL.md language
English
Stars
6
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
7 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
approuter-setup
description
Invoke this skill to set up the SAP Application Router (approuter) for Cloud Foundry ONLY for apps that serve static UI (HTML/CSS/JS under webapp/) or require authentication (web.xml <auth-method>/<security-constraint>, or XSUAA). A bare web.xml on an API-only, no-auth backend (a servlet returning JSON/text) does NOT need an approuter — do not invoke this skill for it. Creates the approuter directory with package.json, xs-app.json, and optional extended middleware for SAML2/Basic Auth handling.
disable-model-invocation
false
allowed-tools
Read, Edit, Write, Bash, Grep, Glob
# Application Router Setup Set up the SAP Application Router (approuter) as the single entry-point for your Cloud Foundry application. ## Purpose The SAP Application Router is a key infrastructure component for web-facing applications running in Cloud Foundry on SAP BTP. It is used to: - **Serve static content:** Deliver HTML, CSS, and JavaScript files directly to the client. - **Authenticate users:** Trigger authentication processes via XSUAA to ensure secure access. - **Rewrite URLs:** Modify URLs of requests to match routing rules. - **Forward or proxy requests:** Redirect client requests to the appropriate backend services. - **Propagate user information:** Include user data (user ID, roles) when forwarding requests to backend services. In the Neo environment, the platform handled routing and authentication implicitly. In Cloud Foundry, you must explicitly set up the approuter as a separate Node.js module. ## Detection > **Do NOT invoke this skill based on the presence of `web.xml` alone.** Almost every Java servlet application has a `web.xml`. An approuter is only needed when the application requires user-facing authentication or serves static web content. Headless backend apps (REST APIs, mail, persistence, document management, keystore) must NOT have an approuter added — it creates an unbacked module and breaks deploy. This skill applies **only** if at least one of these explicit auth/UI signals is present: ### Explicit authentication signal in web.xml ```xml <!-- auth-method is FORM or XSUAA --> <auth-method>FORM</auth-method> <!-- OR security-constraint definitions --> <security-constraint> <web-resource-collection> <web-resource-name>Protected</web-resource-name> <url-pattern>/protected/*</url-pattern> </web-resource-collection> </security-constraint> ``` ```bash # Detect auth signals grep -l "auth-method\|security-constraint" src/main/webapp/WEB-INF/web.xml ``` ### UserProvider usage in Java source ```bash grep -rl "UserProvider\|getUserPrincipal\|isUserInRole" src/main/java/ ``` ### Static web content served to users ```bash # HTML files in webapp/ — indicates a UI that needs an entry point find . -name "*.html" -path "*/webapp/*" | head -5 ``` ### User explicitly requests approuter setup - "Set up approuter" - "Add application router" - "Configure approuter for my app" ## When this skill does NOT apply — do not add an approuter > 🛑 **The presence of `web.xml` alone does NOT mean you need an approuter.** > An approuter's job is serving static UI content and/or fronting XSUAA > authentication. A **standalone, API-only backend with no authentication** does > not need one — and adding it is actively harmful: the standard approuter's > `xs-app.json` binds XSUAA, so the module fails to boot with > `Cannot find service uaa in environment: No service matches uaa` > (and `ias`), taking down an otherwise-working app. > > **Skip this skill (no approuter) when ALL of these hold:** > - No authentication: `web.xml` has no `<auth-method>` and no > `<security-constraint>`, and there is no XSUAA `<resource-ref>`. > - No served UI: no `.html`/`.css`/`.js` under `webapp/` (a servlet that only > returns JSON/text is API-only). > - The app is reached directly (e.g. a mail-sender, a REST/JDBC backend). > > In that case the migrated app is a single `java.tomcat` module bound directly > to whatever services it needs (destination, connectivity, credstore, …) — > **no `approuter/` module, no `xs-app.json`, no uaa/ias resources.** The > `mail`, `persistence`, and `document-management` scenarios are exactly this > shape (JSON/REST backends with a `web.xml` but no auth and no served UI). > > ⚠️ **If you add an approuter anyway, you own the route mapping.** The standard > `xs-app.json` rewrites the public path onto the backend destination. If the > WAR's context root is non-root (e.g. `document-management.war` serves at > `/document-management`, not `/`) and the `xs-app.json` route target does not > account for it, **every request 404s** — the approuter forwards `/ecm` to the > backend as `/document-management/ecm`, which does not exist. This is silent: > the app is healthy, the approuter is healthy, and every call returns 404. > Skipping the approuter entirely (the app is reached directly) avoids the > whole class of mismatch. ## Backend-only apps — XSUAA without approuter If the application is a headless REST/servlet app (no user-facing UI, no FORM auth) but still needs XSUAA for technical token validation (e.g. machine-to-machine calls), **do not add an approuter**. Bind XSUAA directly to the Java module: ```yaml # mtad.yaml — XSUAA-only, no approuter module modules: - name: <app-name> type: java.tomcat path: target/<artifactId>.war parameters: buildpack: sap_java_buildpack_jakarta memory: 1024M disk-quota: 1024M properties: ENABLE_SECURITY_JAVA_API_V2: true JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jre.SAPMachineJRE']" JBP_CONFIG_SAP_MACHINE_JRE: "{ version: 25.+ }" TARGET_RUNTIME: tomcat SET_LOGGING_LEVEL: 'ROOT: INFO' requires: - name: <app-name>-xsuaa resources: - name: <app-name>-xsuaa type: org.cloudfoundry.managed-service parameters: service: xsuaa service-plan: application path: xs-security.json ``` > The `authentication-xsuaa` skill handles the `xs-security.json` and XSUAA service resource. An approuter is only needed when you want the platform to enforce authentication *before* requests reach the backend. ## Prerequisites Before invoking this skill, ensure you have invoked: 1. **sdk-replacement** - `Use the sdk-replacement skill` - Sets up SAP Cloud SDK - REQUIRED before this skill ## Decision: Standard vs Extended Approuter > **Default to Standard.** The extended approuter is rarely needed. XSUAA handles SAML, OIDC, and Basic auth challenges natively. Only choose Extended if you have **confirmed** that your backend application explicitly sends `WWW-Authenticate` or `com.sap.cloud.security.logout` headers in HTTP responses that require client-side redirect interception. Before proceeding, determine which approuter variant your application needs: | Scenario | Variant | Start Command | |----------|---------|---------------| | All endpoints require XSUAA auth | **Standard** | `node node_modules/@sap/approuter/approuter.js` | | Mix of authenticated + public endpoints | **Standard** | `node node_modules/@sap/approuter/approuter.js` | | Neo app used SAML2 + Basic Auth (common) | **Standard** | `node node_modules/@sap/approuter/approuter.js` | | Backend explicitly sends `WWW-Authenticate` headers that need client-side redirect | **Extended** | `node server.js` | | Backend explicitly sends `com.sap.cloud.security.logout` headers | **Extended** | `node server.js` | > **Rule of thumb:** Use **Standard** in almost all cases. The fact that the Neo application used SAML2 or Basic Auth does **not** mean you need the extended approuter — XSUAA handles these authentication methods natively. Only choose Extended if the user explicitly confirms the backend triggers `WWW-Authenticate` challenge headers that must be intercepted and redirected by middleware (this is very rare in CF). ## Transformation Steps ### Step 1: Create the approuter Directory Create a new `approuter/` directory in the project root: ```bash mkdir -p approuter ``` ### Step 2: Create package.json #### Standard Approuter Create `approuter/package.json` — see [assets/package.json](assets/package.json): ```json { "name": "approuter", "dependencies": { "@sap/approuter": "^16.0.0" }, "scripts": { "start": "node node_modules/@sap/approuter/approuter.js" } } ``` #### Extended Approuter If you need the extended approuter, use a custom start script instead: ```json { "name": "approuter", "dependencies": { "@sap/approuter": "^16.0.0" }, "scripts": { "start": "node server.js" } } ``` > **Note:** The version of `@sap/approuter` may be updated over time. Check the [NPM package page](https://www.npmjs.com/package/@sap/approuter) for the latest version during the actual migration. ### Step 3: Create xs-app.json (Routing Configuration) The `xs-app.json` file defines how the approuter routes requests to your backend application. #### Standard xs-app.json Create `approuter/xs-app.json` — see [assets/xs-app.json](assets/xs-app.json): ```json { "authenticationMethod": "route", "routes": [ { "source": "^/(.*)$", "target": "/$1", "destination": "<destination-name>", "authenticationType": "xsuaa", "csrfProtection": false } ] } ``` > **Note:** Replace `<destination-name>` with the name of the destination you will wire in `mtad.yaml` (e.g., `backend-app-destination`). This destination proxies requests from the approuter to your Java backend. > **Context path:** `sap_java_buildpack_jakarta` always serves the app at `/` (ROOT context). It additionally registers the app at `/<warName>/` only if `web.xml` contains a servlet mapped to `url-pattern: /`. In most migrated apps there is no such mapping, so only `/` works — approuter targets should use `/$1` without any `/<artifactId>/` prefix. #### xs-app.json with Protected and Public Routes If your application has both protected and public endpoints: ```json { "authenticationMethod": "route", "routes": [ { "source": "^/protected(/.*)?$", "target": "/protected$1", "destination": "<destination-name>", "authenticationType": "xsuaa", "csrfProtection": false }, { "source": "^/(.*)$", "target": "/$1", "destination": "<destination-name>", "authenticationType": "none", "csrfProtection": false } ], "logout": { "logoutEndpoint": "/logout", "logoutPage": "/" } } ``` > **Important:** Routes are matched top-to-bottom. Place more specific routes (e.g., `/protected`) above the catch-all route (`^(/.*)`). #### Preserving Servlet Path Prefixes If the backend's `web.xml` maps the JAX-RS/Jersey servlet to a specific prefix (e.g., `/webapi/*`), the approuter route **must** preserve that prefix in the target. A common mistake is writing a route that strips the prefix: ```json // WRONG — strips /webapi, backend servlet mapping won't match { "source": "^/webapi(/.*)?$", "target": "$1" } // CORRECT — preserves /webapi, backend servlet mapping matches { "source": "^/webapi(/.*)?$", "target": "/webapi$1" } ``` On Neo there was no approuter — the browser called the backend directly and the servlet mapping matched naturally. On CF, the approuter sits in front, so its target must forward the path the backend expects. If the backend also serves static content (HTML, CSS, JS) from the WAR's `webapp/` directory, do NOT change the servlet mapping to `/*` as this causes Jersey to intercept static resource requests, returning "Not Found". #### Extended xs-app.json (Rare — Backend-Initiated Auth Challenges Only) > **Warning:** Only use this template if you have explicitly verified the need for extended approuter (see decision table above). In most migrations, the standard xs-app.json is sufficient. Using the extended configuration unnecessarily introduces routing complexity and potential crashes. For applications where the backend explicitly sends `WWW-Authenticate` or `com.sap.cloud.security.logout` headers — see [assets/xs-app-extended.json](assets/xs-app-extended.json): ```json { "authenticationMethod": "route", "routes": [ { "source": "^/protected(/.*)?$", "target": "/protected$1", "destination": "<destination-name>", "authenticationType": "xsuaa", "csrfProtection": false }, { "source": "^/authentication/endpoint(/.*)", "target": "$1", "destination": "<destination-name>", "authenticationType": "xsuaa", "csrfProtection": false }, { "source": "^/basic/authentication/endpoint(/.*)", "target": "$1", "destination": "<destination-name>", "authenticationType": "basic", "csrfProtection": false }, { "source": "^/logout/callback", "target": "/logout", "destination": "<destination-name>", "authenticationType": "none", "csrfProtection": false }, { "source": "^/(.*)$", "target": "/$1", "destination": "<destination-name>", "authenticationType": "none", "csrfProtection": false } ], "logout": { "logoutEndpoint": "/logout", "logoutPage": "/logout/callback" } } ``` ### Step 4: Create Extended Approuter Files (if needed) Skip this step if using the standard approuter. #### approuter/server.js
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub