Shopify App Development
A primer on how RecoApp functions within the Shopify App workflows and ecosystem - summarising the core system components, authentication flow, and deployment architecture.
Overview
RecoApp is a Shopify App Store app that embeds into the Shopify Admin and tracks customer behavior via web pixels. Understanding how these pieces fit together is essential for working with the codebase.
Core System Parts
1. recoapp-shopify Remix Server Application
What it is: A Node.js server application built with Remix that provides the merchant-facing admin interface.
Where it runs: On our Coolify hosting infrastructure, but deployable anywhere (Vercel, Fly.io, etc.)
Key responsibilities:
- OAuth authentication with Shopify
- Session management and storage
- Admin UI that embeds in Shopify Admin
- Server-side API calls to Shopify Admin API
Files with .server.ts suffix:
- Remix convention for server-only code
- Stripped completely from browser bundles at build time
- Safe to include secrets, database connections, and Node.js APIs
- Not React Server Components - different concept
2. recoapp-web-pixel Web Pixel Extension
What it is: Client-side JavaScript that runs on merchant storefronts to track customer behavior.
Where it runs: In customer browsers on live storefronts, loaded from Shopify's CDN
Key responsibilities:
- Track product views, cart additions, checkouts
- Send events to
recoapp-apifor processing - No access to our server - runs completely independently
Deployment: Uploaded to Shopify via shopify app deploy
3. recoapp-api NestJS API Backend
What it is: A separate API server that processes pixel events and provides data to the Shopify app frontend.
Where it runs: On our Coolify hosting infrastructure, but deployable as a container (Cloud Run, Fly.io, etc.)
Key responsibilities:
- Receive and validate pixel tracking events
- Store event data in Xata database
- Provide data endpoints for the Shopify app
Authentication & Sessions
OAuth Flow
When a merchant installs your app, Shopify's OAuth 2.0 flow establishes authenticated access:
Session Storage
Implementation: recoapp-shopify/app/session-storage.server.ts
Sessions are stored in the database using Prisma with a custom storage layer that sanitizes session IDs for Xata compatibility.
Session ID format:
offline_shop-name_myshopify_com
Why "offline_"?
The offline_ prefix indicates the token type, not the environment:
- Offline tokens = Permanent access tokens that never expire
- Used for background jobs, webhooks, and API calls when merchants aren't logged in
- One per shop, persists until app is uninstalled
- Standard for App Store apps
Shopify also has "online tokens" that expire after 24 hours and are tied to specific users, but RecoApp uses offline tokens exclusively for continuous tracking and background processing.
ID Sanitization:
Xata doesn't allow periods in record IDs, so session IDs like offline_shop.myshopify.com are converted to offline_shop_myshopify_com before storage.
Access Token Lifecycle
Installation (Day 1):
OAuth completes → Offline token received → Stored in database
Token: shpat_abc123... (permanent)
Long-term (Days/Months/Years):
Token remains valid indefinitely ✓
App continues to:
- Make Shopify API calls
- Process webhooks
- Track pixel events
Token expires only when:
- Merchant uninstalls the app
- App permissions change (requires re-authorization)
- Merchant manually revokes access
See the Authentication documentation for detailed information on all authentication mechanisms, including pixel API keys, session tokens, and webhook validation.
Development vs Deployment
Two Separate Deployments
RecoApp requires deploying to two different hosting environments:
1. Our Remix App Server (Coolify)
What you deploy:
- Remix application code
- Server-side routes and API handlers
- Session storage and database connections
How you deploy:
# Build the app
pnpm run build
# Deploy to Coolify (or hosting platform)
# Method varies by platform: git push, Docker, etc.
URL: https://recoapp-shopify-<env>.craftapplied.dev
2. Shopify Extensions (Shopify's Infrastructure)
What you deploy:
- Web pixel extension code
- App configuration metadata
- Extension settings schema
How you deploy:
# Build and upload extensions to Shopify
shopify app deploy
# or pnpm run deploy
Where it runs: Shopify hosts and serves extensions globally from their CDN
Deployment Architecture
Runtime Flow
When a merchant uses RecoApp app:
- Merchant opens Shopify Admin
- Clicks RecoApp app
- Embedded iframe loads from Coolify
- RecoApp Remix server authenticates the session
- Renders the admin interface
When a customer shops:
- Customer visits merchant's storefront
- Shopify loads RecoApp web pixel from Shopify's CDN
- Pixel tracks events in the browser
- Events sent directly to
recoapp-apiendpoints - API stores events in Xata
recoapp-dbdatabase
Development Workflow
Local Development
Start Shopify app:
cd recoapp-shopify
pnpm run dev
This starts:
- Remix dev server (default: port
50518) - Shopify CLI tunnel for OAuth callbacks
- Hot module reloading for code changes
Start API server (separate terminal):
cd recoapp-api
pnpm run start:dev
Start docs site (separate terminal):
cd recoapp-docs
pnpm run start
Testing in a Dev Store
- Create a development store in RecoApp Shopify Partner account
- Install RecoApp app from Partner Dashboard (not App Store)
- Test OAuth flow and session creation
- Configure web pixel settings with test shop ID and API key
- Browse storefront to trigger pixel events
Environment Configuration
RecoApp local .env files must have matching credentials:
recoapp-shopify/.env:
SHOPIFY_API_KEY=<from-partner-dashboard>
SHOPIFY_API_SECRET=<from-partner-dashboard>
RECOAPP_INTERNAL_API_KEY=<must-match-api>
recoapp-api/.env:
SHOPIFY_API_KEY=<must-match-shopify-app>
SHOPIFY_API_SECRET=<must-match-shopify-app>
INTERNAL_API_KEY=<must-match-shopify-app>
ENCRYPTION_KEY=<32-characters-exactly>
The SHOPIFY_API_KEY and SHOPIFY_API_SECRET must be identical in both apps, and INTERNAL_API_KEY must match RECOAPP_INTERNAL_API_KEY.
Production Deployment
Pre-Deployment Checklist
- Environment variables configured on hosting platforms
- Database migrations applied
- HTTPS enforced on all endpoints
- API keys rotated from development values
- Extension settings schema finalized
Deployment Steps
1. Deploy API Server:
cd recoapp-api
pnpm run build
# Deploy to your hosting platform
2. Deploy Shopify App:
cd recoapp-shopify
pnpm run build
# Deploy to Coolify or hosting platform
3. Deploy Extensions to Shopify:
cd recoapp-shopify
pnpm run deploy
4. Create App Version in Partner Dashboard:
- Review extension changes
- Submit for app review (if public listing)
- Test in a production store before public release
Post-Deployment Verification
Check app loads:
curl https://your-app.coolify.app/health
Check API responds:
curl https://your-api.coolify.app/health
Test OAuth flow:
- Install app in a test store
- Verify session created in database
- Confirm embedded app loads in Shopify Admin
Test pixel tracking:
- Configure pixel settings in Shopify Admin
- Browse storefront as a customer
- Verify events appear in
recoapp-apilogs and database
Common Gotchas
Session IDs with Periods
Xata doesn't allow periods in record IDs. Always use the custom session storage which sanitizes IDs automatically.
Extension Changes Require Redeployment
Editing TypeScript files in extensions/recoapp-web-pixel/src/ requires running pnpm run deploy to upload changes to Shopify. Local changes won't appear on storefronts until deployed.
Pixel Settings Must Be Configured
After installing the app, merchants must manually configure:
shopify_shop_id- Shopify GID format:gid://shopify/Shop/XXXXXrecoapp_api_key- Generated during shop creation
These are required for pixel events to authenticate properly.
API Keys Must Match
The SHOPIFY_API_KEY, SHOPIFY_API_SECRET, and internal API keys must match between recoapp-shopify and recoapp-api or authentication will fail.
Further Reading
Official Shopify Documentation:
RecoApp Documentation:
- Authentication - Detailed authentication mechanisms
- Installation: recoapp-shopify - Shopify app setup
- Installation: recoapp-api - API setup
Remix Documentation: