- Full Node.js application with Shopify GraphQL API integration - Compare At price support for promotional pricing - Comprehensive error handling and retry logic - Progress tracking with markdown logging - Complete test suite with unit and integration tests - Production-ready with proper exit codes and signal handling
6.9 KiB
6.9 KiB
Design Document
Overview
The Shopify Price Updater is a Node.js command-line script that uses Shopify's GraphQL Admin API to bulk update product prices based on tag filtering. The script will be built using the @shopify/shopify-api package and will implement proper error handling, rate limiting, and progress tracking.
Architecture
The application follows a modular architecture with clear separation of concerns:
shopify-price-updater/
├── src/
│ ├── config/
│ │ └── environment.js # Environment variable loading and validation
│ ├── services/
│ │ ├── shopify.js # Shopify API client and authentication
│ │ ├── product.js # Product querying and updating logic
│ │ └── progress.js # Progress logging functionality
│ ├── utils/
│ │ ├── price.js # Price calculation utilities
│ │ └── logger.js # Console and file logging utilities
│ └── index.js # Main application entry point
├── .env.example # Environment variable template
├── package.json
└── Progress.md # Generated progress log file
Compare At Price Workflow
The Compare At price functionality works as follows:
- Price Capture: When a product variant is processed, the current price is captured as the "original price"
- Price Calculation: The new price is calculated by applying the percentage adjustment to the original price
- Compare At Assignment: The original price (before adjustment) is set as the Compare At price
- Simultaneous Update: Both the new price and Compare At price are updated in a single GraphQL mutation
- Progress Tracking: All three values (original, new, and Compare At) are logged for transparency
This creates a promotional pricing display where customers can see both the current discounted price and the original price for comparison.
Components and Interfaces
Environment Configuration (config/environment.js)
- Loads and validates environment variables from
.envfile - Required variables:
SHOPIFY_SHOP_DOMAIN,SHOPIFY_ACCESS_TOKEN,TARGET_TAG,PRICE_ADJUSTMENT_PERCENTAGE - Validates that percentage is a valid number and tag is not empty
- Exports configuration object with validated values
Shopify Service (services/shopify.js)
- Initializes Shopify GraphQL client using
@shopify/shopify-api - Handles authentication and session management
- Provides methods for executing GraphQL queries and mutations
- Implements retry logic for rate limiting (HTTP 429 responses)
Product Service (services/product.js)
- Implements GraphQL queries to fetch products by tag
- Handles pagination for large product sets using cursor-based pagination
- Implements GraphQL mutations for price and Compare At price updates
- Manages product variant price updates (products can have multiple variants)
- Sets Compare At price to the original price before applying percentage adjustment
- Automatically formats tag queries with "tag:" prefix if not already present
Progress Service (services/progress.js)
- Creates and manages Progress.md file
- Logs operation start, product updates, errors, and completion summary
- Appends to existing file with timestamps for multiple runs
- Formats progress entries in markdown for readability
Price Utilities (utils/price.js)
- Calculates new prices based on percentage adjustment
- Handles rounding to appropriate decimal places for currency
- Validates price ranges and handles edge cases (negative prices, zero prices)
- Provides functions to prepare price update objects with both price and Compare At price values
Logger Utilities (utils/logger.js)
- Provides consistent logging to console and progress file
- Implements different log levels (info, warning, error)
- Formats output for both human readability and progress tracking
Data Models
Product Query Response
query getProductsByTag($query: String!, $first: Int!, $after: String) {
products(first: $first, after: $after, query: $query) {
edges {
node {
id
title
tags
variants(first: 100) {
edges {
node {
id
price
compareAtPrice
}
}
}
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
Product Update Mutation
mutation productVariantUpdate($input: ProductVariantInput!) {
productVariantUpdate(input: $input) {
productVariant {
id
price
compareAtPrice
}
userErrors {
field
message
}
}
}
Configuration Object
{
shopDomain: string,
accessToken: string,
targetTag: string,
priceAdjustmentPercentage: number
}
Progress Entry
{
timestamp: Date,
productId: string,
productTitle: string,
variantId: string,
oldPrice: number,
newPrice: number,
compareAtPrice: number,
status: 'success' | 'error',
errorMessage?: string
}
Error Handling
Rate Limiting
- Implement exponential backoff for HTTP 429 responses
- Use Shopify's rate limit headers to determine retry timing
- Maximum retry attempts: 3 with increasing delays (1s, 2s, 4s)
API Errors
- Parse GraphQL userErrors from mutation responses
- Log specific error messages for debugging
- Continue processing remaining products when individual updates fail
Network Errors
- Catch and handle network connectivity issues
- Implement retry logic for transient network failures
- Graceful degradation when API is temporarily unavailable
Data Validation
- Validate product data before attempting updates
- Skip products with invalid price data
- Handle edge cases like products without variants
Progress Tracking Errors
- Continue script execution even if progress logging fails
- Fallback to console-only logging if file writing fails
- Ensure critical operations aren't blocked by logging issues
Testing Strategy
Unit Tests
- Test price calculation utilities with various percentage values
- Test environment configuration loading and validation
- Test GraphQL query and mutation builders
- Test error handling scenarios with mocked API responses
Integration Tests
- Test Shopify API authentication with test credentials
- Test product querying with known test data
- Test price update operations in Shopify development store
- Test progress logging functionality
End-to-End Tests
- Test complete workflow with development store
- Verify price updates are applied correctly
- Test error recovery and continuation scenarios
- Validate progress logging accuracy
Manual Testing Checklist
- Test with various percentage values (positive, negative, decimal)
- Test with products having multiple variants
- Test with large product sets requiring pagination
- Test error scenarios (invalid credentials, network issues)
- Verify Progress.md file generation and formatting