Building Custom Shopify Apps with AI: A Developer's Complete Guide
Step-by-step guide to building AI-powered Shopify apps using Node.js, React, and modern AI APIs. Learn authentication, webhooks, and AI integration.
Shopify's app ecosystem is thriving, and AI-powered apps command premium pricing. This guide walks you through building a production-ready Shopify app with AI capabilities.
Prerequisites
Before starting, ensure you have: Node.js 18+, a Shopify Partner account, basic understanding of React and Node.js, and an OpenAI API key (or alternative LLM provider).
Project Setup with Shopify CLI
# Install Shopify CLI
npm install -g @shopify/cli @shopify/app
# Create new app
shopify app init
# Select:
# - Template: Node.js with React
# - App name: ai-product-assistant
cd ai-product-assistant
npm installUnderstanding the App Structure
ai-product-assistant/
├── app/ # Remix frontend
│ ├── routes/
│ │ ├── app._index.tsx # Main app page
│ │ └── api.*.tsx # API routes
│ └── shopify.server.ts # Shopify auth
├── extensions/ # Shopify extensions
├── prisma/ # Database schema
└── shopify.app.toml # App configurationSetting Up AI Integration
// app/lib/ai.server.ts
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export async function analyzeProduct(product: ShopifyProduct) {
const completion = await openai.chat.completions.create({
model: "gpt-4-turbo-preview",
messages: [
{
role: "system",
content: `You are an e-commerce optimization expert.
Analyze products and provide actionable recommendations.`
},
{
role: "user",
content: `Analyze this product and suggest improvements:
Title: ${product.title}
Description: ${product.body_html}
Price: ${product.variants[0].price}
Tags: ${product.tags}`
}
],
response_format: { type: "json_object" }
});
return JSON.parse(completion.choices[0].message.content);
}
export async function generateSEODescription(
product: ShopifyProduct,
keywords: string[]
) {
const completion = await openai.chat.completions.create({
model: "gpt-4-turbo-preview",
messages: [
{
role: "user",
content: `Write an SEO-optimized product description.
Product: ${product.title}
Current description: ${product.body_html}
Target keywords: ${keywords.join(', ')}
Requirements:
- 150-200 words
- Include keywords naturally
- Focus on benefits
- Include a call to action`
}
]
});
return completion.choices[0].message.content;
}Building the App Frontend
// app/routes/app._index.tsx
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData, useFetcher } from "@remix-run/react";
import {
Page,
Layout,
Card,
Button,
DataTable,
Badge,
} from "@shopify/polaris";
import { authenticate } from "../shopify.server";
export async function loader({ request }: LoaderFunctionArgs) {
const { admin } = await authenticate.admin(request);
const response = await admin.graphql(`
query {
products(first: 50) {
edges {
node {
id
title
status
totalInventory
priceRangeV2 {
minVariantPrice {
amount
}
}
}
}
}
}
`);
const { data } = await response.json();
return json({ products: data.products.edges });
}
export default function Index() {
const { products } = useLoaderData<typeof loader>();
const fetcher = useFetcher();
const analyzeProduct = (productId: string) => {
fetcher.submit(
{ productId, action: "analyze" },
{ method: "post", action: "/api/ai-analyze" }
);
};
return (
<Page title="AI Product Assistant">
<Layout>
<Layout.Section>
<Card>
<DataTable
columnContentTypes={["text", "text", "numeric", "text"]}
headings={["Product", "Status", "Inventory", "Actions"]}
rows={products.map(({ node }) => [
node.title,
<Badge status={node.status === "ACTIVE" ? "success" : "warning"}>
{node.status}
</Badge>,
node.totalInventory,
<Button onClick={() => analyzeProduct(node.id)}>
Analyze with AI
</Button>
])}
/>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}Creating the AI Analysis API Route
// app/routes/api.ai-analyze.tsx
import { json, type ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import { analyzeProduct, generateSEODescription } from "../lib/ai.server";
export async function action({ request }: ActionFunctionArgs) {
const { admin } = await authenticate.admin(request);
const formData = await request.formData();
const productId = formData.get("productId") as string;
const actionType = formData.get("action") as string;
// Fetch product details
const response = await admin.graphql(`
query getProduct($id: ID!) {
product(id: $id) {
id
title
bodyHtml
tags
variants(first: 1) {
edges {
node {
price
}
}
}
}
}
`, { variables: { id: productId } });
const { data } = await response.json();
const product = data.product;
if (actionType === "analyze") {
const analysis = await analyzeProduct(product);
return json({ success: true, analysis });
}
if (actionType === "generate-description") {
const keywords = formData.get("keywords") as string;
const description = await generateSEODescription(
product,
keywords.split(",")
);
return json({ success: true, description });
}
return json({ error: "Unknown action" }, { status: 400 });
}Adding Webhook Handlers
// app/routes/webhooks.tsx
import { type ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import { analyzeProduct } from "../lib/ai.server";
import db from "../db.server";
export async function action({ request }: ActionFunctionArgs) {
const { topic, shop, payload } = await authenticate.webhook(request);
switch (topic) {
case "PRODUCTS_CREATE":
// Auto-analyze new products
const analysis = await analyzeProduct(payload);
await db.productAnalysis.create({
data: {
shop,
productId: payload.admin_graphql_api_id,
analysis: JSON.stringify(analysis),
createdAt: new Date(),
},
});
break;
case "PRODUCTS_UPDATE":
// Re-analyze on significant changes
// ...
break;
case "APP_UNINSTALLED":
// Cleanup shop data
await db.session.deleteMany({ where: { shop } });
break;
}
return new Response();
}Building a Theme App Extension
<!-- extensions/ai-recommendations/blocks/recommendations.liquid -->
{% schema %}
{
"name": "AI Recommendations",
"target": "section",
"settings": [
{
"type": "range",
"id": "products_count",
"min": 2,
"max": 8,
"default": 4,
"label": "Number of products"
}
]
}
{% endschema %}
<div id="ai-recommendations" data-product-id="{{ product.id }}">
<h3>{{ block.settings.title | default: "You May Also Like" }}</h3>
<div class="recommendations-grid">
<!-- Populated by JavaScript -->
</div>
</div>
<script>
(async () => {
const container = document.getElementById('ai-recommendations');
const productId = container.dataset.productId;
const response = await fetch(
`https://your-app.com/api/recommendations?product=${productId}&shop={{ shop.permanent_domain }}`
);
const { products } = await response.json();
// Render recommendations...
})();
</script>Deployment and App Store Submission
# Deploy to production
shopify app deploy
# Generate app listing
shopify app release
# Submit for review
# - Ensure GDPR compliance
# - Add privacy policy
# - Record demo video
# - Write compelling listingBest Practices for AI Shopify Apps
1. Cache AI responses to reduce costs and latency.
2. Implement rate limiting to prevent API abuse.
3. Provide fallbacks when AI services are unavailable.
4. Be transparent about AI usage in your app.
5. Handle PII carefully—don't send sensitive data to AI APIs.
NyxaLabs Shopify Development Services
We build custom Shopify apps that leverage AI to solve real merchant problems. From concept to App Store, our team handles the entire development lifecycle. Contact us to discuss your Shopify app idea.