Skip to main content
Clone JTL’s sample Cloud App, connect it to your developer account, and run it locally. By the end, you’ll have a working app talking to the JTL platform. Time: ~15 minutes What you’ll build: A full-stack Cloud App (React frontend + Express backend) that authenticates with the JTL platform and runs inside the App Shell.

Prerequisites

Before you start, make sure you have:
1

Accounts Set Up

You need all three accounts from the Create a Developer Account page:
  • JTL-Wawi account
  • Partner Portal account
  • JTL Hub access
If you haven’t done this yet, go set them up first, as you’ll need the credentials from the Partner Portal in a few minutes.
2

Tools Installed

You’ll need the following tools installed on your machine:
  • Node.js v18 or higher. Run node --version to check your current version
  • npm. Run npm --version to verify installation

1. Create the Project Using Cloud Apps CLI

npm create @jtl-software/cloud-app@latest
Follow the prompts to create your app:
  • Ok to proceed? (y): y
  • App name: my-jtl-app
  • Description: My JTL Sample App
  • Backend: Node.js (Express + TypeScript)
  • Frontend: React (Vite + Tailwind + JTL UI)
Use the Arrow Keys to navigate and Enter to select.

2. Install Dependencies

From the project root, install all dependencies:
cd my-jtl-app
npm install
This installs dependencies for the project.

3. Create your App on the Partner Portal

In the /packages/frontend directory, you’ll find a manifest.json file. This file defines your app’s configuration, including its name, description, icon, and other settings. You can update these values to match your app. For a full reference, see the manifest.json documentation. Next, go to the Partner Portal, create a new app, then paste the contents of your manifest.json into the Manifest Editor and click Register app. Partner Portal app creation screenshot

4. Add your Credentials

Once you register your app, you’ll see your client ID and client secret in the Partner Portal. Copy these values to a safe place. Navigate to the backend package, create your environment file /packages/backend/.env, and add your credentials:
CLIENT_ID=your-client-id-here
CLIENT_SECRET=your-client-secret-here
VariableWhere to find it
CLIENT_IDPartner Portal > your app > client credentials
CLIENT_SECRETPartner Portal > your app > client credentials
Don’t commit your .env file. It contains your client secret. The .gitignore should already exclude it, but double-check before pushing any code.

5. Start the App

Run the following command from the root directory to start the development server:
npm run dev
This starts both the backend and frontend:
ServiceURLPort
Backend (Express API)http://localhost:30053005
Frontend (React App)http://localhost:30043004

6. Open the App

Open http://localhost:3004 in your browser. You should see an instruction page. It’s intentional, as the sample app is designed to run inside the JTL Cloud.

7. Verify the Connection

Your app is now running locally and connected to the JTL platform using your credentials. To verify everything is working:
  1. Open JTL Hub in your browser
  2. Navigate to your Discover apps
  3. You should see your Hello World app listed
Discover apps
  1. Click the Install button to install the app.
Install app on JTL Complete the installation process by clicking the Complete setup button in the installation wizard. Complete setup

8. Fetch Data from JTL-Wawi

Your app is running and connected to the platform, but it’s not pulling real ERP data yet. The template includes a working demo page that queries Wawi through the ERP’s GraphQL API. Let’s walk through how it works.

Open the GraphQL Demo Page

The template ships with a demo page at packages/frontend/src/pages/graphql-demo-page/ that runs several example queries against your JTL-Wawi (items, orders, stock data). To view it, see the Test your App guide.

How it Works

The frontend never talks to the JTL-Wawi API directly. All requests go through your backend, which handles authentication transparently. The backend’s POST /graphql endpoint does five things:
  1. Reads the session token from the X-Session-Token header
  2. Verifies the token and extracts the tenant ID
  3. Obtains an access token using your app’s client credentials
  4. Forwards the GraphQL request to the JTL ERP API with the correct Authorization and X-Tenant-ID headers
  5. Returns the response as-is
The template’s /graphql proxy is not secured beyond session token verification. In a production app, you should add your own authentication layer (e.g., validate the session token against your own user database) to prevent unauthorized access.

The Query Code

The frontend uses graphql-request to send queries through the proxy. Here’s the pattern used in the demo page:
import { GraphQLClient, gql } from 'graphql-request';

// Create a client pointing at your backend's /graphql proxy
function createClient(sessionToken: string) {
	return new GraphQLClient(`${apiUrl}/graphql`, {
		headers: { 'X-Session-Token': sessionToken },
	});
}

// Define your query
const GET_ITEMS = gql`
	query TopItems {
		QueryItems(first: 5, order: [{ stockInOrders: DESC }]) {
			totalCount
			nodes {
				sku
				name
				stockInOrders
				salesPriceGross
			}
		}
	}
`;

// Execute it. Get the session token from AppBridge first
const sessionToken = await appBridge.method.call<string>('getSessionToken');
const client = createClient(sessionToken);
const data = await client.request(GET_ITEMS);
What this does: Gets a fresh session token from the AppBridge, creates a GraphQL client pointing at the backend’s /graphql proxy, and sends a query to fetch the top 5 items sorted by stock in orders. The backend handles all authentication before the request reaches the JTL-Wawi API. A sample response looks like this:
{
	"data": {
		"QueryItems": {
			"totalCount": 674,
			"nodes": [
				{
					"sku": "AR2016041-VKO",
					"name": "Men's T-shirt",
					"stockInOrders": 42,
					"salesPriceGross": 29.99
				},
				{
					"sku": "AR2016041-002",
					"name": "Men's T-shirt orange S",
					"stockInOrders": 38,
					"salesPriceGross": 29.99
				}
			]
		}
	}
}
This demo runs a minimal query to verify the connection. The GraphQL API supports filtering, sorting, pagination, and mutations. Explore the full schema interactively in the GraphQL Playground, or read the Using Platform APIs guide for more patterns.

What Just Happened?

Here’s what’s running under the hood:
  • The React frontend renders your app’s UI (running on port 3004)
  • The Express backend handles authentication and API calls (running on port 3005)
  • The backend uses your client ID and secret to authenticate with JTL via OAuth 2.0
  • Once authenticated, it can call the JTL-Wawi API to read and write data

Project Structure

Here’s what’s inside my-jtl-app/:
my-jtl-app/
├── packages/
│   ├── backend/          # Express API server
│   │   ├── src/          # Server code
│   │   └── .env          # Your credentials (not committed)
│   └── frontend/         # React application
│       ├── src/          # React components
│       └── manifest.json # App manifest
├── package.json          # Monorepo config
└── README.md

Common Issues

The frontend runs on HTTPS, which requires a certificate. In development, this is a self-signed certificate that your browser doesn’t trust. Click “Advanced” → “Proceed to localhost” (Chrome) or “Accept the Risk” (Firefox) to continue. This is safe for local development.
Another process is using the port. Find and stop it:
# macOS / Linux
lsof -i :3004
kill -9 <PID>

# Windows
netstat -ano | findstr :3004
taskkill /PID <PID> /F
Double-check your .env file:
  • Is CLIENT_ID correct? (no extra spaces or quotes)
  • Is CLIENT_SECRET correct?
  • Is the .env file in packages/backend/, not the project root?
If you’re still stuck, regenerate your credentials in the Partner Portal and try again.
Your API request is missing required headers. Verify you’re sending:
  • Authorization: Bearer <accessToken>
  • X-Tenant-ID: <tenantId> (from session token payload)
Make sure the session token is still valid and hasn’t expired.
If you get { "data": null } or empty results:
  • Verify the tenantId is correct (check what’s in your session token)
  • Confirm you have data in your Wawi for the query you’re making
  • Check that your GraphQL query syntax is valid (no missing brackets or commas)

What’s Next?

Your app is running. Here’s where to go from here:

Test your App

Use the cloud environment to test your app with real (test) data.

Using Platform APIs

Use the JTL-Wawi REST and GraphQL APIs, handle responses, and work with tenant-scoped data.

GraphQL Playground

Try queries and mutations interactively against your ERP instance.

Build from Scratch

Want to understand every piece? Build a Cloud App step by step.

App Shell & UI

Integrate deeper with the JTL UI using AppBridge and Platform UI components.