Merge Catalog is a Shopify operations app built for Merge Studio. It helps merchants normalize product descriptions, extract structured product data into metafields, review every proposed change, and apply only explicitly approved updates.
It is deliberately not a general-purpose AI copywriting tool. The product is designed around controlled catalog maintenance, evidence-aware suggestions, and a complete application history.
- A merchant selects up to 25 products with Shopify's Resource Picker.
- Merge Catalog snapshots the current product data before processing.
- Background jobs normalize descriptions and extract selected structured fields.
- The merchant edits, approves, or rejects descriptions and metafields separately.
- A separate background job applies only approved changes through the Shopify Admin GraphQL API.
- History records the result and provenance of every application attempt.
Merge Catalog never writes AI output to Shopify automatically.
- Operational dashboard for catalog jobs and status counts.
- Cleanup jobs limited to 25 products.
- Configurable description normalization and metafield extraction scopes.
- Optional per-shop brand tone guidance.
- Product snapshots captured before processing.
- OpenAI-backed description normalization and structured extraction.
- Zod validation before AI output is persisted.
- Per-field confidence, source, evidence, and review status.
- Manual editing and separate approval for descriptions and metafields.
- Dynamic Shopify metafield mappings and definition management.
- Selective application through Shopify Admin GraphQL mutations.
- Job cancellation and rejection without Shopify writes.
- Apply history with source summaries and Shopify responses.
- Internal per-shop usage and estimated OpenAI cost tracking.
- Shopify embedded app using Remix, React, TypeScript, and Vite.
- Shopify Polaris and App Bridge.
- Shopify Admin GraphQL API.
- Prisma with PostgreSQL 18.
pg-bossfor snapshot, AI-processing, and apply queues.- OpenAI behind an
AiProviderabstraction. zodfor structured AI-output validation.- devenv and Nix for the local development environment.
- Docker Compose for production deployment.
- Nix
- devenv
- A Shopify Partner account and development store
- A Shopify app configuration linked through Shopify CLI
- An OpenAI API key
Codex, Git, curl, and OpenSSL are inherited from the host when available; they are not project-level Nix dependencies.
Enter the reproducible development shell:
devenv shellThe shell provides Node.js 24, pnpm, Shopify CLI, and PostgreSQL 18. Install JavaScript dependencies:
pnpm installCreate the local environment file:
cp .env.example .envReplace all placeholder credentials in .env. devenv supplies the local database URL as:
postgresql://merge_catalog@127.0.0.1:5432/merge_catalog
Start PostgreSQL in a separate terminal:
devenv upGenerate the Prisma client and apply development migrations:
pnpm prisma generate
pnpm prisma migrate devLink the local checkout to the intended Shopify app if needed:
pnpm config:linkStart Shopify development mode:
pnpm devShopify CLI manages authentication, the development tunnel, callback URLs, and the embedded-app launch flow.
| Variable | Required | Purpose |
|---|---|---|
SHOPIFY_API_KEY |
Yes | Shopify app client ID. |
SHOPIFY_API_SECRET |
Yes | Shopify app client secret. |
SHOPIFY_APP_URL |
Yes outside Shopify CLI dev | Public application and callback URL. |
SCOPES |
Yes | Shopify access scopes; defaults to write_products. |
DATABASE_URL |
Yes | PostgreSQL connection used by Prisma and pg-boss. |
OPENAI_API_KEY |
Yes | OpenAI API credential used by the initial AI provider. |
OPENAI_MODEL |
No | Model override; defaults to gpt-4.1-mini. |
HOST |
No | Local runtime host override. |
PORT |
No | Application port; defaults to 3000. |
SHOP_CUSTOM_DOMAIN |
No | Optional custom shop domain. |
INTERNAL_USAGE_VIEW_ENABLED |
No | Enables the internal usage page outside development. |
INTERNAL_USAGE_ALLOWED_SHOPS |
No | Comma-separated allowlist for the internal usage page. |
Never commit .env, Shopify credentials, OpenAI keys, or production database credentials.
pnpm dev # Run Shopify development mode
pnpm test # Run the Vitest suite
pnpm lint # Run ESLint
pnpm build # Build client and server bundles
pnpm exec tsc --noEmit # Type-check without emitting files
pnpm prisma format # Format the Prisma schema
pnpm prisma generate # Generate Prisma Client
pnpm prisma migrate dev # Create or apply development migrationsAfter meaningful backend, Prisma, or route changes, run:
pnpm lint
pnpm build
pnpm exec tsc --noEmitFor Prisma changes, also run pnpm prisma format and pnpm prisma generate. Database changes must always use migrations; do not edit PostgreSQL manually or reintroduce SQLite.
The Remix action that creates a cleanup job only validates the selection and enqueues work. Long-running processing runs through three pg-boss queues:
catalog-cleanup
-> snapshot selected Shopify products
-> catalog-ai-processing
-> validate and persist drafts
-> merchant review
-> catalog-apply-approved
-> apply approved Shopify updates
The main boundaries are intentional:
- Always snapshot before processing.
- Keep batches at or below 25 products.
- Do not run batch processing inside Remix loaders or actions.
- AI processing never writes directly to Shopify.
- Description and metafield approvals remain separate.
- Shopify mutations live behind focused server-side services.
- Only merchant-approved changes are eligible for application.
ApplyResultpreserves status, errors, Shopify responses, and source history.
The core Prisma records are:
ShopandShopUsageCatalogJobandCatalogJobItemProductSnapshotProductDraftandExtractedFieldMetafieldMappingAiRunApplyResult
Snapshots, product JSON, variants, metafields, extracted values, AI inputs and outputs, source summaries, and Shopify responses use PostgreSQL JSONB fields.
- Dashboard
- New Cleanup Job
- Job Progress
- Review Queue
- Metafield Settings
- History and Apply Result detail
- Internal Usage, restricted to development or explicitly allowlisted shops
The root Dockerfile builds a minimal multi-stage Node.js 24 image. The root compose.yaml runs the application with PostgreSQL 18 and expects an existing external cloudflared-net network for reverse-proxy access.
Set the required production variables, create the external network if it does not already exist, and start the stack:
docker network create cloudflared-net
docker compose up -d --buildPrisma Client is generated while the image is built, and the production container applies pending migrations before starting Remix. Keep production secrets outside the repository.
- Contributor and agent instructions
- Roadmap and implementation history
- Launch checklist
- Template dependency history
The v1 product intentionally excludes full catalog synchronization, automatic scheduling, SEO scoring, keyword research, complex rollback, merchant prompt builders, fine-tuning, unlimited batches, and mass automatic metafield-definition creation.