AppBridge Initialization
How you initialize the AppBridge determines whether your app starts reliably or fails intermittently with race conditions.Initialize Before Rendering
Always create the AppBridge instance before your React app renders. If you initialize it inside a component (e.g., inuseEffect), child components may try to access the bridge before it’s ready.
Use Dynamic Imports for SSR Frameworks
If you’re using Next.js or another framework with server-side rendering, the AppBridge SDK fails on the server because it requires a browser environment. Use a client-side provider with dynamic imports.window is not defined errors during server rendering. See the From Scratch quickstart for the full provider pattern.
Handle Initialization Failures
The AppBridge can fail to initialize if your app isn’t running inside the App Shell (e.g., during local development in a regular browser tab). Always catch errors and display a helpful message.Authentication
Token management is the most common source of production issues. These patterns prevent the majority of auth-related failures.Cache Access Tokens
Access tokens are valid for approximately one hour. Requesting a new token on every API call wastes time and puts unnecessary load on the auth server. Cache the token in memory and refresh it before expiry.Retry on 401
Even with caching, tokens can expire unexpectedly (server clock drift, cache invalidation). Retry once with a fresh token before surfacing the error.Never Expose Credentials to the Frontend
YourCLIENT_SECRET must stay on the server. Never include it in frontend code, environment variables prefixed with NEXT_PUBLIC_, or client-side bundles. The frontend authenticates through the AppBridge session token, not client credentials.
Refresh Session Tokens before Sensitive Operations
Session tokens from the AppBridge are short-lived. Request a fresh one before operations that require verified identity, rather than reusing a token obtained minutes ago.API Usage
Efficient API usage keeps your app fast and prevents rate limiting.Request Only the Fields You Need
GraphQL lets you specify exactly which fields to return. Avoid requesting fields your UI doesn’t display. Smaller payloads mean faster responses and less bandwidth.Paginate Large Datasets
Never fetch all records at once. Use cursor-based pagination with a reasonable page size (10-50 items). Fetch the next page only when the user scrolls or clicks “Load more.”Handle GraphQL Errors in the Response Body
GraphQL returns errors inside the JSON body, not as HTTP status codes. A200 OK response can still contain errors. Always check both data and errors.
Error Handling
Consistent error handling prevents your app from crashing and gives merchants useful feedback when things go wrong.Wrap API Calls in try/catch
Every API call can fail. Network issues, expired tokens, rate limits, and server errors all produce exceptions. Catch them and provide meaningful error states.Distinguish Error Types
Different errors require different responses. A network timeout should be retried. A 403 should prompt the merchant to check permissions. A validation error should highlight the problematic field.Show Loading and Error States
Never leave the merchant looking at a blank screen. Show a loading indicator while data is being fetched, and a clear error message if something fails.Security
These practices protect both your app and the merchants who use it.Verify Session Tokens on the Backend
Never trust a session token without verifying it. The frontend can be manipulated. Your backend should verify every session token against JTL’s JWKS before using the tenant ID from it.Validate Tenant ID on Every Request
When your frontend sends a tenant ID to your backend, verify it matches the tenant ID in the verified session token. This prevents one merchant from accessing another merchant’s data.Use HTTPS in Production
All production URLs in your manifest (setupUrl, connectUrl, disconnectUrl, menu item URLs, pane URLs) must use HTTPS. HTTP is acceptable forlocalhost during development only.
Store Credentials Securely
Keep yourCLIENT_SECRET in environment variables. Never commit .env files to version control. Use a secrets manager (AWS Secrets Manager, Vault, Doppler) for production deployments.
Performance
Fast apps get more installs and fewer support tickets.Cache JWKS Responses
JTL’s public keys change infrequently. Fetching them on every session token verification adds latency. Cache the JWKS response and refresh it only when verification fails with a key mismatch.Minimize Frontend Bundle Size
Use dynamic imports for the AppBridge SDK and any heavy dependencies. This reduces your initial page load, which matters because your app loads inside an iframe that already has the full ERP UI rendered around it.Deployment Checklist
Before submitting your app to the App Store, verify each item on this list.Update manifest URLs
Replace all
localhost URLs in your manifest with production HTTPS
URLs. This includes setupUrl, connectUrl, disconnectUrl,
redirectUrl, menu item URLs, and pane URLs.Verify credentials
Confirm your
CLIENT_ID and CLIENT_SECRET are set in your production
environment. Test the token exchange from your deployed server.Test the full install flow
Walk through the complete flow in before deploying: install from JTL
Cloud, complete setup, verify data loads, test all features, then
uninstall and confirm cleanup.
Review error handling
Confirm your app handles token expiry, network failures, and empty data
states without crashing or showing blank screens.
Validate icon URLs
Make sure your manifest’s
icon.light and icon.dark point to publicly
accessible HTTPS URLs, not local paths.Submit to the App Store
Deploy your app and publish it for merchants.
Architecture Overview
Review the full architecture of JTL Cloud Apps.
Error Handling
In-depth error format reference and retry strategies.