AppRouter Setup and xs-app.json

Install @sap/approuter, write an xs-app.json that routes requests to your CAP backend, and run the full AppRouter + CAP stack locally before deploying.

AppRouter Setup and xs-app.json

What AppRouter actually does

AppRouter is an SAP-provided Node.js reverse proxy. It has three jobs:

  1. Authentication gateway — redirects unauthenticated browsers to XSUAA login, then stores the token in a session cookie so users only log in once
  2. Token forwarder — extracts the token from the session and adds Authorization: Bearer <token> to every request it proxies to your backend
  3. Static file server — optionally serves your built SAPUI5 app from the same origin (avoids CORS completely)

Without AppRouter, your SPA would need to implement OAuth2 PKCE itself and handle token refresh — AppRouter does all of that for free.


Create the AppRouter folder

mkdir -p app/router
cd app/router
npm init -y
npm install @sap/approuter

xs-app.json — the routing config

{
  "welcomeFile": "/index.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "source": "^/catalog/(.*)$",
      "target": "/catalog/$1",
      "destination": "srv-api",
      "authenticationType": "xsuaa",
      "csrfProtection": false
    },
    {
      "source": "^/(.*)$",
      "target": "$1",
      "localDir": "webapp",
      "authenticationType": "xsuaa"
    }
  ]
}
Field What it does
source Regex matching incoming request path
target Path to rewrite to (capture groups work)
destination Named destination (defined in env vars or mta.yaml)
localDir Serve static files from this folder (relative to xs-app.json)
authenticationType xsuaa = require login, none = public route
csrfProtection Set to false for pure API routes (OData handles it internally)

package.json start script

{
  "name": "approuter",
  "scripts": {
    "start": "node node_modules/@sap/approuter/approuter.js"
  },
  "dependencies": {
    "@sap/approuter": "^16.x.x"
  }
}

Run locally with default-env.json

AppRouter needs to know where XSUAA and your backend are.
Create app/router/default-env.json (never commit this — add to .gitignore):

{
  "destinations": [
    {
      "name": "srv-api",
      "url": "http://localhost:4004",
      "forwardAuthToken": true
    }
  ],
  "VCAP_SERVICES": {
    "xsuaa": [
      {
        "name": "bookshop-xsuaa",
        "label": "xsuaa",
        "plan": "application",
        "credentials": {
          "clientid": "sb-bookshop...",
          "clientsecret": "...",
          "url": "https://yourtenant.authentication.eu10.hana.ondemand.com",
          "xsappname": "bookshop"
        }
      }
    ]
  }
}

Copy credentials from your XSUAA service key (from BTP Cockpit).

Start the full local stack

Terminal 1 — CAP backend:

cd bookshop
cds watch --profile hybrid

Terminal 2 — AppRouter:

cd app/router
npm start

AppRouter listens on port 5000 by default. Open http://localhost:5000 — you are redirected to XSUAA login, then back to your app.


Change the AppRouter port

PORT=5001 npm start

Or add to default-env.json:

{
  "PORT": 5001
}

The most common AppRouter mistake

Setting "authenticationMethod": "none" globally in xs-app.json for local testing, then forgetting to change it before deploying. You end up with a publicly accessible app with no login. Always use "authenticationMethod": "route" and set individual routes to "authenticationType": "none" for genuinely public paths.

Sign in for progress tracking & Q&A →