Skip to main content

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-api for 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
info

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
tip

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:

  1. Merchant opens Shopify Admin
  2. Clicks RecoApp app
  3. Embedded iframe loads from Coolify
  4. RecoApp Remix server authenticates the session
  5. Renders the admin interface

When a customer shops:

  1. Customer visits merchant's storefront
  2. Shopify loads RecoApp web pixel from Shopify's CDN
  3. Pixel tracks events in the browser
  4. Events sent directly to recoapp-api endpoints
  5. API stores events in Xata recoapp-db database

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

  1. Create a development store in RecoApp Shopify Partner account
  2. Install RecoApp app from Partner Dashboard (not App Store)
  3. Test OAuth flow and session creation
  4. Configure web pixel settings with test shop ID and API key
  5. 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>
warning

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-api logs 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/XXXXX
  • recoapp_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:

Remix Documentation: