@multidots/sanity-plugin-amazon-product-sync
v1.0.8
Published
Sanity Studio plugin to fetch and embed Amazon products by ASIN (PA-API v5), with settings tool and document actions.
Maintainers
Readme
Sanity Plugin: Amazon Product Sync
A comprehensive Sanity Studio plugin for fetching and managing Amazon products using the Amazon Product Advertising API (PA-API v5). This plugin provides seamless integration between Sanity Studio and Amazon's product data with real-time fetching capabilities.
🚀 Features
- Amazon Settings Management: Configure PA-API credentials with a dedicated settings singleton
- Real-time Form Validation: Instant feedback and button state updates using
useFormValue - Product Document Management: Create multiple Amazon product documents with auto-fetching
- Fetch from Amazon Button: Dedicated button component for product data retrieval
- Multi-region Support: US, UK, DE, FR, IT, ES, CA, AU, JP, IN marketplaces
- Streamlined Product Data: Essential fields including title, brand, pricing, images, and more
- TypeScript Support: Full type safety throughout the plugin
- Modern Sanity v4: Built for the latest Sanity Studio architecture
- Secure Configuration: Environment variable-based API endpoint configuration
Amazon Product Sync plugin overview in Sanity Studio
API Settings: https://share.cleanshot.com/sYc88tRMBvHPvsjvt9YT
Product Fields Display Settings: https://share.cleanshot.com/Nrtl8RpdhgJrSr7Lj0j6
Test Connection and Debug Actions: https://share.cleanshot.com/Nrtl8RpdhgJrSr7Lj0j6
📦 Installation
npm install @multidots/sanity-plugin-amazon-product-sync⚙️ Setup
1. Add Plugin to Sanity Config
// sanity.config.ts
import { defineConfig } from 'sanity'
import { structureTool } from 'sanity/structure'
import { visionTool } from '@sanity/vision'
import { amazonProductsPlugin } from '@multidots/sanity-plugin-amazon-product-sync'
import { structure } from './structure'
export default defineConfig({
name: 'default',
title: 'Your Studio',
projectId: 'your-project-id',
dataset: 'production',
plugins: [
structureTool({ structure }),
visionTool(),
amazonProductsPlugin(),
],
schema: {
types: [
// Your existing schemas
],
},
})2. Create Structure Configuration
Create structure.ts in your project root:
// structure.ts
import type { StructureResolver } from 'sanity/structure'
export const structure: StructureResolver = (S) =>
S.list()
.title('Content')
.items([
...S.documentTypeListItems().filter(
(item) => item.getId() &&
!['amazon.settings'].includes(item.getId()!)
),
S.divider(),
S.listItem()
.id('amazonSettings')
.title('Amazon Settings')
.child(
S.document()
.schemaType('amazon.settings')
.documentId('amazon-settings')
),
])3. Update Sanity Config for Document Rules
// sanity.config.ts (add these document rules)
export default defineConfig({
// ... other config
document: {
// Hide singleton types from the global "New document" menu
newDocumentOptions: (prev: any, { creationContext }: any) => {
if (creationContext.type === 'global') {
return prev.filter(
(templateItem: any) =>
!['amazon.settings'].includes(templateItem.templateId)
)
}
return prev
},
// Prevent duplicate/delete on singleton documents
actions: (prev: any, { schemaType }: any) => {
if (['amazon.settings'].includes(schemaType)) {
return prev.filter(({ action }: any) => action !== 'duplicate' && action !== 'delete')
}
return prev
},
},
// ... rest of config
})🌐 Frontend Integration
The plugin requires server-side API routes to handle Amazon PA-API calls due to CORS restrictions and security requirements.
Environment Variables
CRITICAL: You must set up these environment variables in your Sanity Studio root .env.local file:
# .env.local (in Sanity Studio root)
SANITY_STUDIO_AMAZON_API_URL=http://localhost:3001
SANITY_STUDIO_AMAZON_TEST_CONNECTION_PATH=/api/amazon/test-connection
SANITY_STUDIO_AMAZON_FETCH_PRODUCT_PATH=/api/amazon/fetch-productImportant Notes:
- These environment variables are MANDATORY - the plugin will throw errors if they're missing
- The API server URL should point to your Next.js/Express server that handles Amazon API calls
- The paths should match your server-side API route endpoints
- Environment variables must be prefixed with
SANITY_STUDIO_to be accessible in Sanity Studio - NEW: The plugin now validates environment variables at startup and provides clear error messages
Configuration System
The plugin now includes a centralized configuration system (src/lib/config.ts) that:
- Validates environment variables at startup
- Provides clear error messages for missing or invalid configurations
- Ensures secure endpoint configuration without hardcoded URLs
- Supports flexible API server setups for different environments
Required API Routes
You need to implement these endpoints in your frontend:
POST /api/amazon/test-connection- Test PA-API credentialsPOST /api/amazon/fetch-product- Fetch product data by ASIN
Quick Setup with Next.js
1. Install Dependencies
npm install @sanity/client2. Environment Variables
Create .env.local:
# Sanity Configuration
NEXT_PUBLIC_SANITY_PROJECT_ID=your-project-id
NEXT_PUBLIC_SANITY_DATASET=production
SANITY_API_TOKEN=your-sanity-api-token
# API Configuration
NEXT_PUBLIC_API_URL=http://localhost:30013. Create Sanity Client
// lib/sanity.ts
import { createClient } from '@sanity/client'
export const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
apiVersion: '2025-01-01',
useCdn: false,
token: process.env.SANITY_API_TOKEN,
})
export async function getAmazonSettings() {
const query = `*[_type == "amazon.settings"][0]{
accessKey,
secretKey,
region,
partnerTag
}`
return await client.fetch(query)
}📎 Referencing Amazon Products in Your Schemas
You can easily reference Amazon products in your own document schemas using a simple field definition. This allows you to associate Amazon products with blog posts, reviews, comparisons, or any other content type.
Quick Copy-Paste Field
// Copy this field into any schema where you want to reference Amazon products
defineField({
name: 'amazonProduct',
title: 'Choose Product',
type: 'reference',
to: [{ type: 'amazon.product' }],
options: {
filter: ({ document }) => ({
filter: '_type == "amazon.product"',
params: {}
})
},
description: 'Select an Amazon product',
})Field Variations
Single Product (Required):
defineField({
name: 'amazonProduct',
title: 'Choose Product',
type: 'reference',
to: [{ type: 'amazon.product' }],
validation: Rule => Rule.required(),
})Single Product (Optional):
defineField({
name: 'amazonProduct',
title: 'Choose Product',
type: 'reference',
to: [{ type: 'amazon.product' }],
})Multiple Products (Array):
defineField({
name: 'amazonProducts',
title: 'Choose Products',
type: 'array',
of: [
{
type: 'reference',
to: [{ type: 'amazon.product' }],
}
],
validation: Rule => Rule.min(1).max(10),
})Example Usage in Schemas
Blog Post with Amazon Product:
export const blogPostSchema = defineType({
name: 'post',
title: 'Blog Post',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
validation: Rule => Rule.required(),
}),
// Amazon Product Reference
defineField({
name: 'amazonProduct',
title: 'Choose Product',
type: 'reference',
to: [{ type: 'amazon.product' }],
description: 'Select an Amazon product to feature in this blog post',
}),
// ... other fields
],
})Product Review Schema:
export const productReviewSchema = defineType({
name: 'review',
title: 'Product Review',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Review Title',
type: 'string',
validation: Rule => Rule.required(),
}),
// Amazon Product Reference (Required)
defineField({
name: 'amazonProduct',
title: 'Choose Product',
type: 'reference',
to: [{ type: 'amazon.product' }],
validation: Rule => Rule.required(),
description: 'Select the Amazon product being reviewed',
}),
defineField({
name: 'rating',
title: 'Rating',
type: 'number',
options: { range: { min: 1, max: 5, step: 0.5 } },
}),
// ... other fields
],
})Product Comparison Schema:
export const comparisonSchema = defineType({
name: 'comparison',
title: 'Product Comparison',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Comparison Title',
type: 'string',
validation: Rule => Rule.required(),
}),
// Multiple Amazon Products
defineField({
name: 'amazonProducts',
title: 'Choose Products',
type: 'array',
of: [
{
type: 'reference',
to: [{ type: 'amazon.product' }],
}
],
validation: Rule => Rule.required().min(2).max(5),
description: 'Select 2-5 Amazon products to compare',
}),
// ... other fields
],
})Frontend Usage with GROQ Queries
Once you have Amazon product references in your schemas, you can query them using GROQ. Here are comprehensive examples:
Basic Single Product Reference
// Get blog post with Amazon product reference
const query = `*[_type == "post" && defined(amazonProduct)][0]{
_id,
title,
slug,
amazonProduct->{
_id,
asin,
title,
brand,
price,
currency,
url,
images,
features,
lastSyncedAt
}
}`
const post = await sanityClient.fetch(query)
console.log(post.amazonProduct.title) // Amazon product title
console.log(post.amazonProduct.price) // Amazon product priceDirect Product Queries
Get product by ASIN:
const query = `*[_type == "amazon.product" && asin == $asin][0]{
_id, asin, title, brand, price, currency, url, images, features, lastSyncedAt
}`
const product = await sanityClient.fetch(query, { asin: "B09XFQL45V" })Get product by Reference ID:
const query = `*[_type == "amazon.product" && _id == $referenceId][0]{
_id, asin, title, brand, price, currency, url, images, features, lastSyncedAt
}`
const product = await sanityClient.fetch(query, { referenceId: "3f995870-12ea-4d02-b242-ce78abfbf56e" })Unified query (either ASIN or Reference ID):
const query = `*[_type == "amazon.product" && (
($asin != null && asin == $asin) ||
($referenceId != null && _id == $referenceId)
)][0]{
_id, asin, title, brand, price, currency, url, images, features, lastSyncedAt
}`
// Use with ASIN
const productByAsin = await sanityClient.fetch(query, { asin: "B09XFQL45V", referenceId: null })
// Use with Reference ID
const productByRef = await sanityClient.fetch(query, { asin: null, referenceId: "3f995870-12ea-4d02-b242-ce78abfbf56e" })Multiple Products Array
// Get comparison with multiple Amazon products
const query = `*[_type == "comparison"][0]{
title,
amazonProducts[]->{
asin,
title,
brand,
price,
currency,
url,
images[0].url,
features[0..2]
},
"productCount": count(amazonProducts)
}`
const comparison = await sanityClient.fetch(query)
comparison.amazonProducts.forEach(product => {
console.log(`${product.title}: ${product.price}`)
})Filtered Queries
// Find posts featuring specific Amazon product
const postsByProduct = `*[_type == "post" && amazonProduct->asin == $asin]{
title,
slug,
amazonProduct->{
asin,
title,
price
}
}`
const posts = await sanityClient.fetch(postsByProduct, { asin: 'B0F15TM77B' })
// Find posts with products from specific brand
const postsByBrand = `*[_type == "post" && amazonProduct->brand match $brand + "*"]{
title,
amazonProduct->{
asin,
title,
brand,
price
}
}`
const applePosts = await sanityClient.fetch(postsByBrand, { brand: 'Apple' })Product Reviews with Ratings
// Get product reviews with ratings
const reviewsQuery = `*[_type == "review" && defined(amazonProduct)] | order(rating desc){
title,
rating,
pros,
cons,
amazonProduct->{
asin,
title,
brand,
price,
url,
images[0].url
}
}`
const reviews = await sanityClient.fetch(reviewsQuery)
// Get average rating for specific product
const ratingQuery = `{
"product": *[_type == "amazon.product" && asin == $asin][0]{
asin,
title,
price
},
"averageRating": math::avg(*[_type == "review" && amazonProduct->asin == $asin].rating),
"reviewCount": count(*[_type == "review" && amazonProduct->asin == $asin])
}`
const productStats = await sanityClient.fetch(ratingQuery, { asin: 'B0F15TM77B' })Advanced Analysis Queries
// Get comparison with price analysis
const analysisQuery = `*[_type == "comparison"][0]{
title,
amazonProducts[]->{
asin,
title,
brand,
price,
currency
},
"lowestPrice": amazonProducts[]->price | order(@) | [0],
"highestPrice": amazonProducts[]->price | order(@ desc) | [0],
"brands": array::unique(amazonProducts[]->brand)
}`
const analysis = await sanityClient.fetch(analysisQuery)
console.log(`Price range: ${analysis.lowestPrice} - ${analysis.highestPrice}`)TypeScript Interfaces
interface PostWithProduct {
_id: string
title: string
slug: string
amazonProduct: {
asin: string
title: string
brand: string
price: string
currency: string
url: string
images: Array<{ url: string; width: number; height: number }>
features: string[]
}
}
// Use with type safety
const post: PostWithProduct = await sanityClient.fetch(query)For more GROQ query examples, check the examples/groqQueries.ts file in the plugin.
🎨 React Component for Frontend Display
The plugin includes a flexible React component for displaying Amazon products in your frontend applications.
Quick Start
import AmazonProductDisplay from './AmazonProductDisplay'
import { createClient } from '@sanity/client'
const client = createClient({
projectId: 'your-project-id',
dataset: 'production',
apiVersion: '2025-01-01',
useCdn: true,
})
// Option 1: Display product from reference field
function BlogPost({ post }) {
return (
<div>
<h1>{post.title}</h1>
{post.amazonProduct && (
<AmazonProductDisplay
product={post.amazonProduct}
layout="card"
imageSize="medium"
/>
)}
</div>
)
}
// Option 2: Display product by ASIN lookup
function ProductPage({ asin }) {
return (
<AmazonProductDisplay
asin={asin}
client={client}
layout="horizontal"
imageSize="large"
onProductClick={(product) => {
console.log('Product clicked:', product.title)
}}
/>
)
}Component Props
interface AmazonProductDisplayProps {
// Data source (choose one)
product?: AmazonProduct // Direct product data
asin?: string // ASIN for lookup
referenceId?: string // Reference ID for lookup
client?: SanityClient // Required when using asin or referenceId
// Display options
showFeatures?: boolean // Show feature list (default: true)
showImages?: boolean // Show product image (default: true)
showPrice?: boolean // Show pricing (default: true)
showBrand?: boolean // Show brand name (default: true)
showCTA?: boolean // Show "View on Amazon" button (default: true)
// Styling options
layout?: 'horizontal' | 'vertical' | 'card' // Layout style (default: 'card')
imageSize?: 'small' | 'medium' | 'large' // Image size (default: 'medium')
className?: string // Custom CSS classes
ctaText?: string // Custom CTA button text (default: 'View on Amazon')
// Development options
debug?: boolean // Enable debug mode (default: false)
// Interaction
onProductClick?: (product: AmazonProduct) => void // Click handler
}Usage Examples
🎯 Dual Parameter Support
The component supports 3 different ways to display products:
1. By ASIN Lookup:
import { AmazonProductByASIN } from '@/components/AmazonProductDisplay'
import { sanityClient } from '@/lib/sanity-client'
<AmazonProductByASIN
asin="B09XFQL45V"
client={sanityClient}
layout="card"
debug={true}
/>2. By Reference ID Lookup:
import { AmazonProductByReferenceId } from '@/components/AmazonProductDisplay'
import { sanityClient } from '@/lib/sanity-client'
<AmazonProductByReferenceId
referenceId="3f995870-12ea-4d02-b242-ce78abfbf56e"
client={sanityClient}
layout="card"
debug={true}
/>3. Unified Component (accepts any parameter):
import AmazonProductDisplay from '@/components/AmazonProductDisplay'
import { sanityClient } from '@/lib/sanity-client'
// Option A: ASIN lookup
<AmazonProductDisplay asin="B09XFQL45V" client={sanityClient} />
// Option B: Reference ID lookup
<AmazonProductDisplay referenceId="3f995870..." client={sanityClient} />
// Option C: Direct product data
<AmazonProductDisplay product={productData} />🔍 Single GROQ Query
All lookup methods use one optimized GROQ query:
*[_type == "amazon.product" && (
($asin != null && asin == $asin) ||
($referenceId != null && _id == $referenceId)
)][0]{
_id, asin, title, brand, price, currency, url, images, features, lastSyncedAt
}4. Blog Post with Featured Product:
function BlogPostWithProduct({ slug }) {
const [post, setPost] = useState(null)
useEffect(() => {
const query = `*[_type == "post" && slug.current == $slug][0]{
title, content,
amazonProduct->{ asin, title, brand, price, url, images, features }
}`
client.fetch(query, { slug }).then(setPost)
}, [slug])
return (
<article>
<h1>{post?.title}</h1>
{post?.amazonProduct && (
<AmazonProductDisplay
product={post.amazonProduct}
layout="horizontal"
imageSize="large"
/>
)}
</article>
)
}Product Comparison Grid:
function ProductComparison({ comparisonId }) {
const [comparison, setComparison] = useState(null)
useEffect(() => {
const query = `*[_type == "comparison" && _id == $comparisonId][0]{
title,
amazonProducts[]->{ asin, title, brand, price, url, images, features }
}`
client.fetch(query, { comparisonId }).then(setComparison)
}, [comparisonId])
return (
<div>
<h1>{comparison?.title}</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{comparison?.amazonProducts.map((product) => (
<AmazonProductDisplay
key={product.asin}
product={product}
layout="card"
showFeatures={false}
/>
))}
</div>
</div>
)
}Direct ASIN Lookup:
function ProductWidget({ asin }) {
return (
<div className="bg-gray-50 p-4 rounded">
<h3>Recommended Product</h3>
<AmazonProductDisplay
asin={asin}
client={client}
layout="horizontal"
imageSize="small"
showFeatures={false}
/>
</div>
)
}Product Review with Rating:
function ProductReview({ reviewId }) {
const [review, setReview] = useState(null)
useEffect(() => {
const query = `*[_type == "review" && _id == $reviewId][0]{
title, rating, pros, cons,
amazonProduct->{ asin, title, brand, price, url, images }
}`
client.fetch(query, { reviewId }).then(setReview)
}, [reviewId])
return (
<div className="grid grid-cols-2 gap-8">
<AmazonProductDisplay
product={review?.amazonProduct}
layout="card"
ctaText="Buy Now"
/>
<div>
<h1>{review?.title}</h1>
<div className="rating">Rating: {review?.rating}/5</div>
{/* Review content */}
</div>
</div>
)
}Styling
The component includes default Tailwind CSS classes and can be customized with:
- CSS Import:
import './AmazonProductDisplay.css'- Custom Classes:
<AmazonProductDisplay
className="my-custom-product-card shadow-lg"
// ... other props
/>- Layout Variants:
card- Card layout with border and shadowhorizontal- Side-by-side image and contentvertical- Stacked image above content
🚀 Quick Start: Frontend Integration
Next.js Copy & Paste Setup
For immediate setup, copy the complete working example:
# 1. Copy the complete Next.js example files
cp examples/complete-nextjs-example/page.tsx src/app/page.tsx
cp examples/components/AmazonProductDisplay.tsx src/components/
cp examples/frontend-integration/sanity-client-setup.ts src/lib/sanity-client.ts
# 2. Install required dependencies
npm install @sanity/client @sanity/image-url
# 3. Configure environment variables
cp examples/complete-nextjs-example/env-template.txt .env.local
# Edit .env.local with your actual Sanity project details
# 4. Start development server
npm run devVerify Setup
- Visit
http://localhost:3000(or your configured port) - You should see:
- ✅ "Product by ASIN Lookup" section
- ✅ "Product by Reference ID Lookup" section
- ✅ Debug panels showing component state
- ✅ Manual fetch buttons for testing
Available Files & Examples
Complete Working Examples:
examples/complete-nextjs-example/- 🚀 Full Next.js integrationpage.tsx- Complete page demonstrating ASIN & Reference ID lookupspackage.json- All required dependenciesenv-template.txt- Environment variables templateSETUP_GUIDE.md- 📖 Detailed step-by-step setup guide
Individual Components:
examples/components/AmazonProductDisplay.tsx- Main component with dual parameter supportexamples/components/AmazonProductDisplay.css- Optional stylingexamples/frontend-integration/sanity-client-setup.ts- Production-ready Sanity client
Schema Integration:
examples/productReferenceField.ts- Reusable reference field snippetsexamples/exampleSchema.ts- Blog post & comparison schema examplesexamples/groqQueries.ts- Comprehensive GROQ query examples
API Integration (Optional):
examples/frontend-integration/amazon-client.ts- Amazon PA-API clientexamples/frontend-integration/nextjs-api-route.ts- API route examplesexamples/frontend-integration/fetch-product-api-route.ts- Product fetch API
Troubleshooting:
examples/environment-setup.md- Environment setup & common issues- Built-in debug mode in components
🎯 Usage
1. Configure Amazon Settings
Open your Sanity Studio
Go to "Amazon Settings" from the sidebar
Fill in your Amazon PA-API credentials:
- Region: Select your marketplace (US, UK, DE, etc.)
- Access Key: Your PA-API access key
- Secret Key: Your PA-API secret key
- Partner Tag: Your Amazon Associate tag
- Test ASIN: An ASIN for testing (e.g.,
B0F15TM77B) - Cache Hours: Duration to cache results (1-168 hours)
Click "Test API Connection" to verify your setup
2. Create Amazon Products
Go to "Amazon Products" from the sidebar
Click "Add new" to create a product document
Enter an ASIN number in the ASIN field
Navigate to the "Actions" tab
Click "Fetch from Amazon" button
The document will be auto-populated with:
- Product title (with auto-generated slug)
- Brand name
- Pricing information
- Primary product image
- Product description
- Product type and status
- Amazon permalink
- Last sync timestamp
Review and edit the data as needed
Click "Publish" when ready
NEW: Product titles now automatically generate URL-friendly slugs!
https://share.cleanshot.com/n2tnCdSDwsW11cwYH7dq
Creating a new Amazon product document
3. Manual Entry
You can also manually enter product information without fetching from Amazon. All fields are editable.
📋 Schema Reference
Amazon Settings (amazon.settings)
API Settings:
region- Amazon marketplace regionaccessKey- PA-API access keysecretKey- PA-API secret keypartnerTag- Amazon Associate tagasinNumber- Test ASIN for API validationcacheHours- Cache duration (1-168 hours)
Display Settings:
showTitle- Show/hide product titleshowImage- Show/hide product imageshowFeatures- Show/hide feature listshowPrice- Show/hide pricingshowCtaLink- Show/hide CTA link
Actions:
- "Test API Connection" - Verify credentials with real-time form validation
- "Debug Document" - View document state and form values
NEW: Real-time form validation using useFormValue - buttons enable/disable instantly as you type!
https://share.cleanshot.com/P2LbmkdNCJcwJFf14CHC
Amazon Settings actions panel with Test API Connection button
Amazon Product (amazon.product)
Product Information:
asin- Amazon Standard Identification Numbertitle- Product titleslug- Auto-generated URL-friendly slug from titlebrand- Brand/manufacturer namepermalink- Direct Amazon product URLshortDescription- Product description (HTML stripped)type- Product type: 'simple', 'variable', 'grouped', 'external'featured- Featured product statussku- Stock keeping unit
Pricing:
regularPrice- Regular pricesalePrice- Sale price (if on sale)price- Current selling price
Media & Ratings:
primaryImage- Primary product image URLaverageRating- Average customer rating (0-5)ratingCount- Number of customer ratingsstockStatus- Stock status: 'instock', 'outofstock', 'onbackorder'
Metadata:
lastSyncedAt- Timestamp of last Amazon sync
Actions:
- "Fetch from Amazon" - Auto-populate from Amazon API
🆕 Recent Improvements (Latest Update)
✨ What's New
- Real-time Form Validation: Settings now use
useFormValuefor instant feedback and button state updates - Streamlined Schema: Simplified product schema with essential fields only
- Component Consolidation: Removed redundant components for cleaner architecture
- Secure Configuration: Environment variable-based API endpoint configuration
- Auto-slug Generation: Automatic slug generation from product titles
🔧 Key Changes Made
AmazonSettingsActions.tsx:
- Replaced complex state management with real-time form values
- Uses
useFormValuehooks for immediate validation - Buttons enable/disable in real-time as you type
Schema Simplification:
- Removed complex fields like
description(Portable Text) - Added essential fields like
slug,type,featured,stockStatus - Streamlined pricing fields for better usability
- Removed complex fields like
Component Cleanup:
- Removed
AmazonAsinInput.tsx(redundant functionality) - Removed
AmazonProductFetch.tsx(unused component) - Kept
AmazonFetchButton.tsxas the primary fetch component
- Removed
Configuration System:
- Added
src/lib/config.tsfor centralized environment variable handling - Replaced hardcoded URLs with configurable endpoints
- Added validation for required environment variables
- Added
🏗️ Component Architecture
Current Components
The plugin now uses a streamlined component architecture:
- AmazonSettingsActions.tsx - Settings actions with real-time validation
- AmazonFetchButton.tsx - Primary fetch component for product documents
- Configuration System - Centralized environment variable handling
Removed Components
The following components were removed for cleaner architecture:
- ❌ AmazonAsinInput.tsx - Redundant functionality
- ❌ AmazonProductFetch.tsx - Unused component
Component Responsibilities
- AmazonSettingsActions: Handles API testing and debugging with real-time form values
- AmazonFetchButton: Fetches product data and updates documents
- Config System: Manages environment variables and API endpoints
🛠️ Development
Build the Plugin
cd plugins/@multidots/sanity-plugin-amazon-product-sync
npm install
npm run buildStart API Server
cd your-frontend-app
npm install
npm run devStart Sanity Studio
cd your-sanity-project
npm run dev🔧 Troubleshooting
Common Issues
Frontend Component Issues
"No product available" despite correct ASIN
- Problem: Component shows debug info but never fetches data
- Solution: Enable debug mode and check browser console
<AmazonProductByASIN asin="B09XFQL45V" client={sanityClient} debug={true} // Shows debug panel with manual fetch button />- Click the "🔄 Manual Fetch" button to test the Sanity client directly
Environment variables not working
- Problem:
process.env.NEXT_PUBLIC_SANITY_PROJECT_IDis undefined - Solution: Ensure
.env.localexists with correct variables
# .env.local NEXT_PUBLIC_SANITY_PROJECT_ID=your-project-id NEXT_PUBLIC_SANITY_DATASET=production SANITY_API_TOKEN=your-token- Use
NEXT_PUBLIC_prefix for client-side variables
- Problem:
CORS errors when fetching from Sanity
- Problem: Browser blocks requests to Sanity API
- Solution: Use correct client configuration
export const sanityClient = createClient({ projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!, dataset: 'production', apiVersion: '2025-01-01', useCdn: true, // Important for client-side // NO token needed for public documents })useEffect not triggering automatically
- Problem: Component doesn't fetch data on mount
- Solution: Add debug logging to see which conditions fail
useEffect(() => { console.log('useEffect conditions:', { hasInitialProduct: !!initialProduct, hasAsin: !!asin, hasClient: !!client }) }, [asin, client, initialProduct])
API Issues
"Amazon API credentials not configured"
- Verify your Sanity API token has read access to
amazon.settings - Check that amazon.settings document exists in Sanity Studio
- Ensure token is available server-side only
- Verify your Sanity API token has read access to
"InvalidSignature" error
- Check your PA-API access key and secret key
- Verify your system clock is accurate
- Ensure you're using the correct region
Product documents not accessible
- Test with direct GROQ query:
curl -X POST "https://YOUR_PROJECT_ID.api.sanity.io/v2025-01-01/data/query/production" \ -H "Content-Type: application/json" \ -d '{"query": "*[_type == \"amazon.product\"][0]{asin, title}"}'- Ensure documents are published (not drafts)
- Check document permissions in Sanity Studio
Debug Mode
The component includes built-in debugging tools:
// Enable debug mode to see detailed information
<AmazonProductDisplay
asin="B09XFQL45V"
client={sanityClient}
debug={process.env.NODE_ENV === 'development'}
/>Debug features:
- Console logging of all fetch attempts
- Visual debug panel showing component state
- Manual fetch button to test Sanity client
- Full product data logging
Development Workflow
Test Sanity Connection First
import { testSanityConnection } from './lib/sanity-client-setup' testSanityConnection('B09XFQL45V').then(result => { console.log('Sanity test:', result) })Create Test Products See
examples/environment-setup.mdfor the scriptUse Debug Mode
<AmazonProductByASIN debug={true} />Check Browser Console
- Look for 🎬, 🔍, 📦, ✅, ❌ emoji logs
- Manual fetch button provides direct testing
📚 Additional Resources
- Amazon Product Advertising API Documentation
- AWS Signature Version 4
- Sanity Client Documentation
- Next.js API Routes
📄 License
MIT
🤝 Support
For issues and questions:
- Check the troubleshooting section above
- Verify your configuration matches the examples
- Check browser console and API server logs
- Ensure your Amazon PA-API account is active
Note: This plugin requires an active Amazon Product Advertising API account and valid credentials. The PA-API has rate limits and approval requirements.
