AINode.jsAutomation

AI Product Description Generator

TT
TopicTrick Team
AI Product Description Generator

AI Product Description Generator

Stop writing product descriptions manually for thousands of SKUs. This tool takes product specs, features, and target keywords and generates benefit-focused, SEO-optimised copy — then pushes it directly to Shopify via the Admin API.

This is Tool 31 of the Build 50 AI Automation Tools course.


What You'll Build

  • POST /describe — specs in, polished product description out
  • Multiple tone modes: luxury, technical, budget, playful
  • SEO fields: meta title, meta description, keyword density
  • Shopify bulk update integration

Setup

bash
mkdir ai-product-desc && cd ai-product-desc
npm init -y
npm install express openai axios dotenv
bash
# .env
OPENAI_API_KEY=sk-your-key-here
SHOPIFY_STORE=your-store.myshopify.com
SHOPIFY_ACCESS_TOKEN=shpat_your-token
PORT=3000

Description Generation Service

js
// src/services/descriptionService.js
import OpenAI from 'openai';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const TONE_CONFIGS = {
  luxury: {
    style: 'sophisticated, exclusive, aspirational — focus on craftsmanship and prestige',
    avoid: 'cheap, affordable, deal, discount',
    cta: 'Indulge in excellence',
  },
  technical: {
    style: 'precise, specification-forward, feature-benefit focused for knowledgeable buyers',
    avoid: 'fluffy adjectives, vague claims',
    cta: 'Engineered for performance',
  },
  budget: {
    style: 'value-focused, practical, clear benefits for cost-conscious shoppers',
    avoid: 'overpromising, luxury language',
    cta: 'Get more for less',
  },
  playful: {
    style: 'fun, conversational, emoji-friendly, Gen-Z aware',
    avoid: 'corporate jargon, overly formal language',
    cta: 'You deserve this',
  },
  default: {
    style: 'clear, benefit-focused, friendly and professional',
    avoid: 'excessive superlatives, vague claims',
    cta: 'Shop now',
  },
};

export async function generateProductDescription(product, options = {}) {
  const {
    tone = 'default',
    primaryKeyword = '',
    secondaryKeywords = [],
    brand = '',
    targetAudience = 'general consumers',
    includeShortDescription = true,
  } = options;

  const toneConfig = TONE_CONFIGS[tone] || TONE_CONFIGS.default;
  const keywordNote = primaryKeyword
    ? `Primary keyword: "${primaryKeyword}" — include naturally in the first 100 words and at least twice overall.
Secondary keywords: ${secondaryKeywords.join(', ') || 'none'} — weave in where relevant.`
    : '';

  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `You are an expert ecommerce copywriter.
Write compelling product descriptions that convert browsers into buyers.

Tone: ${toneConfig.style}
Avoid: ${toneConfig.avoid}
Brand: ${brand || 'unspecified'}
Target audience: ${targetAudience}
${keywordNote}

Structure:
1. Opening hook (1 sentence — lead with the main benefit, not a feature)
2. Problem or desire (1-2 sentences)
3. Feature-benefit pairs (3-5 bullet points: "Feature — because benefit")
4. Social proof signal or use-case sentence
5. CTA closing sentence

Return ONLY a JSON object:
{
  "title": "SEO-optimised product title",
  "shortDescription": "1-2 sentence compelling short description for search results",
  "longDescription": "full HTML product description with <p> and <ul><li> tags",
  "bulletPoints": ["feature — benefit", "feature — benefit"],
  "metaTitle": "meta title under 60 chars",
  "metaDescription": "meta description 150-160 chars with keyword",
  "tags": ["suggested product tags"],
  "primaryKeywordDensity": "estimated keyword density in long description"
}`,
      },
      {
        role: 'user',
        content: `Product details:\n${JSON.stringify(product, null, 2)}`,
      },
    ],
    temperature: 0.5,
    response_format: { type: 'json_object' },
  });

  return JSON.parse(response.choices[0].message.content);
}

export async function rewriteDescription(existingDescription, productSpecs, tone = 'default') {
  const toneConfig = TONE_CONFIGS[tone] || TONE_CONFIGS.default;

  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `You are an expert ecommerce copywriter. Rewrite the existing product description to be more compelling.
Tone: ${toneConfig.style}. Preserve all factual product information. Improve clarity and conversion focus.
Return JSON with same fields as a new description.`,
      },
      {
        role: 'user',
        content: `Existing description:\n${existingDescription}\n\nProduct specs:\n${JSON.stringify(productSpecs, null, 2)}`,
      },
    ],
    temperature: 0.5,
    response_format: { type: 'json_object' },
  });

  return JSON.parse(response.choices[0].message.content);
}

Shopify Integration

js
// src/services/shopifyService.js
import axios from 'axios';

const shopifyClient = axios.create({
  baseURL: `https://${process.env.SHOPIFY_STORE}/admin/api/2024-01`,
  headers: {
    'X-Shopify-Access-Token': process.env.SHOPIFY_ACCESS_TOKEN,
    'Content-Type': 'application/json',
  },
});

export async function getShopifyProducts(limit = 50) {
  const { data } = await shopifyClient.get(`/products.json?limit=${limit}&fields=id,title,body_html,tags,variants`);
  return data.products;
}

export async function updateShopifyProduct(productId, description) {
  const { data } = await shopifyClient.put(`/products/${productId}.json`, {
    product: {
      id: productId,
      body_html: description.longDescription,
      title: description.title,
      tags: description.tags?.join(', '),
      metafields: [
        {
          namespace: 'seo',
          key: 'title',
          value: description.metaTitle,
          type: 'single_line_text_field',
        },
        {
          namespace: 'seo',
          key: 'description',
          value: description.metaDescription,
          type: 'single_line_text_field',
        },
      ],
    },
  });
  return data.product;
}

Server

js
// src/server.js
import 'dotenv/config';
import express from 'express';
import pLimit from 'p-limit';
import { generateProductDescription, rewriteDescription } from './services/descriptionService.js';
import { getShopifyProducts, updateShopifyProduct } from './services/shopifyService.js';

const app = express();
app.use(express.json());

app.post('/describe', async (req, res, next) => {
  try {
    const { product, tone, primaryKeyword, secondaryKeywords, brand, targetAudience } = req.body;
    if (!product) return res.status(400).json({ error: 'Product details required' });

    const description = await generateProductDescription(product, {
      tone, primaryKeyword, secondaryKeywords, brand, targetAudience,
    });
    res.json({ success: true, ...description });
  } catch (err) { next(err); }
});

app.post('/rewrite', async (req, res, next) => {
  try {
    const { existingDescription, productSpecs, tone } = req.body;
    if (!existingDescription) return res.status(400).json({ error: 'existingDescription required' });

    const description = await rewriteDescription(existingDescription, productSpecs, tone);
    res.json({ success: true, ...description });
  } catch (err) { next(err); }
});

// Bulk update Shopify products
app.post('/shopify/bulk-update', async (req, res, next) => {
  try {
    const { limit: count = 10, tone, brand, dryRun = true } = req.body;
    const products = await getShopifyProducts(count);

    const limiter = pLimit(3);
    const results = await Promise.all(
      products.map(product =>
        limiter(async () => {
          const description = await generateProductDescription(
            { name: product.title, existingDescription: product.body_html },
            { tone, brand }
          );
          if (!dryRun) {
            await updateShopifyProduct(product.id, description);
          }
          return { productId: product.id, title: product.title, updated: !dryRun, description };
        })
      )
    );

    res.json({ success: true, dryRun, processedCount: results.length, results });
  } catch (err) { next(err); }
});

app.use((err, _req, res, _next) => res.status(500).json({ error: err.message }));
app.listen(process.env.PORT ?? 3000, () => console.log('Product description generator running'));

Testing

bash
curl -X POST http://localhost:3000/describe \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "name": "Titanium Insulated Water Bottle",
      "material": "Grade 5 Titanium",
      "capacity": "750ml",
      "features": ["24-hour cold, 12-hour hot", "leak-proof lid", "BPA-free", "scratch-resistant"],
      "weight": "180g",
      "price": 89.99
    },
    "tone": "luxury",
    "primaryKeyword": "titanium water bottle",
    "targetAudience": "outdoor enthusiasts and premium lifestyle buyers"
  }'

Build 50 AI Automation Tools — Tool 31 of 50

Product description generator is live. Continue to Tool 32 to build an AI newsletter writer from RSS feeds.


    Summary

    • Tone configs centralise brand voice — change one object to update tone across all generated descriptions
    • Feature–benefit pairs enforce copy structure that converts: lead with value, not features
    • dryRun mode in the Shopify bulk update lets you preview changes before committing to the catalog
    • Meta fields update SEO title and description via Shopify metafields API — no manual entry
    • For 10,000+ SKU catalogs, batch process overnight with cron and store results in a review queue

    Continue to Tool 32: AI Newsletter Writer from RSS →