Skip to main content

Target multiple Extend apps in one App UI

Last updated on October 29, 2026
note

Current Extend commands are on AGS CLI for Extend. The steps below use ags extend.

Overview​

info

Generate code from Extend app specs covers the single-service workflow: the react template, one swaggers.json entry, one generated client, and VITE_AB_EXTEND_APP_NAME set at runtime to route requests to the correct Extend app. This page requires that foundation. Read it first.

When you need a single App UI to interact with more than one Extend service, use the react-multiple-extend-apps template. It extends the codegen workflow covered in Generate code from Extend app specs to support multiple Swagger specs. Each Extend app gets its own generated client under a separate subdirectory, and no single VITE_AB_EXTEND_APP_NAME environment variable is needed because each generated client already has the correct service path baked in.

Prerequisites​

All prerequisites from Extend App UI apply here.

info

In Public Cloud, all admin roles can navigate to Extend > My Extend Apps > App UI and click Open UI to launch a deployed App UI. But when the App UI calls an Extend app endpoint that requires a custom permission, only accounts with the Studio Admin role currently have the required permission. Accounts with only the Game Admin or View Only role receive a 403. A future release will include support for these roles. For details, see Default roles in Public Cloud.

In Private Cloud, you can freely add permissions to the role attached to your user. See Add permissions to a role.

In addition, you need two or more deployed Extend Service Extension or Event Handler apps. Take note of each app's Service URL, which you'll find in the Admin Portal on each Extend app's detail page.

The examples on this page use two instances of the Extend Tournament System, so you'll see tournamentapi and secondtournamentapi throughout the template. Replace those with your own services' details as you follow along.

Quick start​

Scaffold the React multiple-extend-apps template, which comes with multi-target codegen already configured:

ags extend clone-template --template "Extend App UI :: react-multiple-extend-apps :: JavaScript" -d react-multiple-extend-apps
cd react-multiple-extend-apps

This template extends the react template with a two-entry swaggers.json, an abcodegen.config.ts tuned for multi-service output, and a reference implementation showing two tournament services managed from a single UI.

Set your environment variables using the values from Prerequisites. The AGS CLI reads AGS_BASE_URL, AGS_CLIENT_ID, AGS_CLIENT_SECRET, and AGS_NAMESPACE from the environment. Export them for login and command defaults, then sign in once with ags auth login before you run ags extend commands:

export AGS_BASE_URL='<YourAGSBaseURL>'
export AGS_CLIENT_ID='<YourClientID>'
export AGS_CLIENT_SECRET='<YourClientSecret>'
export AGS_NAMESPACE='<YourGameNamespace>'
export APP_UI_NAME='<YourAppUIName>'

ags auth login --grant client-credentials --no-input

In the same terminal session, register the App UI, generate .env.local, and install dependencies:

ags extend app-ui create --namespace $AGS_NAMESPACE --json "{\"name\":\"$APP_UI_NAME\"}"
ags extend app-ui setup-env --namespace $AGS_NAMESPACE --name $APP_UI_NAME
npm install

The setup-env command generates .env.local with authentication variables and VITE_AB_NAMESPACE pre-filled from your shell environment. In the multi-app template, VITE_SINGLE_EXTEND_APP_ONLY is not set: each generated client already includes its own service path from the Swagger spec, so no single VITE_AB_EXTEND_APP_NAME env var is needed.

In swaggers.json, replace each placeholder URL with the corresponding service's Swagger URL. Each entry maps to [serviceName, aliasName, swaggerFileOutput, swaggerURL]:

[
["tournamentapi", "tournamentapi", "tournament.json", "https://<YourFirstServiceURL>/apidocs/api.json"],
["secondtournamentapi", "secondtournamentapi", "secondtournament.json", "https://<YourSecondServiceURL>/apidocs/api.json"]
]
  • serviceName: the name used as the output subdirectory under src/codegen/ for generated files.
  • aliasName: must match serviceName. It is unused in this setup because shouldProduceIndexFiles is disabled.
  • swaggerFileOutput: the filename used to cache the downloaded spec locally under swaggers/.
  • swaggerURL: the Swagger v2 URL to fetch. For Extend apps, this is /apidocs/api.json under the service URL.

Then run codegen:

npm run codegen

This generates typed TypeScript files into src/codegen/tournamentapi/ and src/codegen/secondtournamentapi/. The react-multiple-extend-apps template outputs generated files under src/codegen/ with a subdirectory per module, keeping multiple generated clients isolated from one another.

Your generated clients are now ready. To start a local dev server with hot reload:

npm run dev

Open the URL shown in your terminal (typically http://localhost:5173). The reference implementation includes a nav bar with links to First tournament service and Second tournament service, each fetching data from a different Extend app instance. To deploy to AccelByte infrastructure, upload your App UI:

ags extend app-ui upload --namespace $AGS_NAMESPACE --name $APP_UI_NAME

After the upload completes, go to the Admin Portal and navigate to Extend > My Extend Apps > App UI. Click Open UI on the app's table row. Anyone with access to your game namespace can see the App UI.

The react-multiple-extend-apps template App UI displayed in the AccelByte Gaming Services (AGS) Admin Portal

Codegen configuration​

abcodegen.config.ts in the multiple-extend-apps template ships with these defaults:

{
// Skips generating index files. These are useful for npm libraries,
// but unnecessary when the generated code is used locally.
shouldProduceIndexFiles: false,

// Splits generated output into a subdirectory per service name.
// This is required when generating clients for multiple Extend apps
// so that each service's types and query functions stay isolated.
splitOutputByServiceName: true,

// Overrides specific types that don't resolve cleanly in TypeScript.
overrideAsAny: {
ProtobufAny: true
}
}

Two things differ from the single-app template. First, splitOutputByServiceName: true is required. Without it, generated files from different services would collide in the same output directory. Second, basePath is not set to '' here. In the single-app template, basePath: '' strips the service path from the generated client so VITE_AB_EXTEND_APP_NAME can re-inject it at runtime. When targeting multiple apps, each client keeps its own basePath from the downloaded spec, so each client is already bound to its own service. No env var needed at runtime.

Next steps​