From shopify-pack
Provides Shopify app reference architecture using Remix, Prisma session storage, official templates, project structure, OAuth, webhooks, and extensions.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin shopify-packThis skill is limited to using the following tools:
Production-ready architecture based on Shopify's official Remix app template. Covers project structure, session storage with Prisma, extension architecture, and the recommended app patterns.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Production-ready architecture based on Shopify's official Remix app template. Covers project structure, session storage with Prisma, extension architecture, and the recommended app patterns.
my-shopify-app/
├── app/
│ ├── routes/
│ │ ├── app._index.tsx # Main app dashboard
│ │ ├── app.products.tsx # Product management page
│ │ ├── app.settings.tsx # App settings
│ │ ├── auth.$.tsx # OAuth catch-all route
│ │ ├── auth.login/
│ │ │ └── route.tsx # Login page
│ │ └── webhooks.tsx # Webhook handler
│ ├── shopify.server.ts # Shopify API config (singleton)
│ ├── db.server.ts # Database connection
│ └── root.tsx
├── extensions/
│ ├── theme-app-extension/ # Theme blocks for Online Store
│ │ ├── blocks/
│ │ │ └── product-rating.liquid
│ │ └── locales/
│ ├── checkout-ui/ # Checkout UI extension
│ └── product-discount/ # Shopify Function
├── prisma/
│ ├── schema.prisma # Database schema
│ └── migrations/
├── shopify.app.toml # App configuration
├── shopify.web.toml # Web process config
├── remix.config.js
└── package.json
// app/shopify.server.ts — the heart of the app
import "@shopify/shopify-app-remix/adapters/node";
import { AppDistribution, shopifyApp } from "@shopify/shopify-app-remix/server";
import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import prisma from "./db.server";
const shopify = shopifyApp({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
appUrl: process.env.SHOPIFY_APP_URL!,
scopes: process.env.SHOPIFY_SCOPES?.split(","),
apiVersion: "2024-10",
distribution: AppDistribution.AppStore, // or SingleMerchant
sessionStorage: new PrismaSessionStorage(prisma),
webhooks: {
APP_UNINSTALLED: {
deliveryMethod: "http",
callbackUrl: "/webhooks",
},
PRODUCTS_UPDATE: {
deliveryMethod: "http",
callbackUrl: "/webhooks",
},
},
hooks: {
afterAuth: async ({ session }) => {
// Register webhooks after successful auth
shopify.registerWebhooks({ session });
},
},
future: {
unstable_newEmbeddedAuthStrategy: true,
},
});
export default shopify;
export const apiVersion = "2024-10";
export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;
export const authenticate = shopify.authenticate;
export const unauthenticated = shopify.unauthenticated;
export const login = shopify.login;
export const registerWebhooks = shopify.registerWebhooks;
export const sessionStorage = shopify.sessionStorage;
// prisma/schema.prisma
datasource db {
provider = "sqlite" // or "postgresql" for production
url = env("DATABASE_URL")
}
model Session {
id String @id
shop String
state String
isOnline Boolean @default(false)
scope String?
expires DateTime?
accessToken String
userId BigInt?
firstName String?
lastName String?
email String?
accountOwner Boolean @default(false)
locale String?
collaborator Boolean? @default(false)
emailVerified Boolean? @default(false)
}
// Your app's custom models
model ProductSync {
id String @id @default(cuid())
shop String
productId String
lastSynced DateTime @default(now())
status String @default("pending")
@@unique([shop, productId])
}
// app/routes/app.products.tsx
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { authenticate } from "../shopify.server";
import { Page, Layout, Card, DataTable } from "@shopify/polaris";
export async function loader({ request }: LoaderFunctionArgs) {
const { admin } = await authenticate.admin(request);
// admin.graphql is a pre-authenticated GraphQL client
const response = await admin.graphql(`{
products(first: 25, sortKey: UPDATED_AT, reverse: true) {
edges {
node {
id
title
status
totalInventory
priceRangeV2 {
minVariantPrice { amount currencyCode }
}
}
}
}
}`);
const data = await response.json();
return json({ products: data.data.products.edges.map((e: any) => e.node) });
}
export default function Products() {
const { products } = useLoaderData<typeof loader>();
return (
<Page title="Products">
<Layout>
<Layout.Section>
<Card>
<DataTable
columnContentTypes={["text", "text", "numeric", "text"]}
headings={["Title", "Status", "Inventory", "Price"]}
rows={products.map((p: any) => [
p.title,
p.status,
p.totalInventory,
`$${p.priceRangeV2.minVariantPrice.amount}`,
])}
/>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
{% comment %} extensions/theme-app-extension/blocks/product-rating.liquid {% endcomment %}
{% schema %}
{
"name": "Product Rating",
"target": "section",
"settings": [
{
"type": "range",
"id": "max_stars",
"label": "Maximum Stars",
"min": 1,
"max": 5,
"default": 5
},
{
"type": "color",
"id": "star_color",
"label": "Star Color",
"default": "#FFD700"
}
]
}
{% endschema %}
<div class="product-rating" style="--star-color: {{ block.settings.star_color }}">
{% assign rating = product.metafields.custom.rating.value | default: 0 %}
{% for i in (1..block.settings.max_stars) %}
<span class="star {% if i <= rating %}filled{% endif %}">★</span>
{% endfor %}
<span class="rating-text">{{ rating }}/{{ block.settings.max_stars }}</span>
</div>
<style>
.product-rating .star { color: #ccc; font-size: 1.2em; }
.product-rating .star.filled { color: var(--star-color); }
</style>
| Issue | Cause | Solution |
|---|---|---|
| Session not found | DB not migrated | Run npx prisma migrate dev |
| Auth redirect loop | Missing APP_UNINSTALLED handler | Implement webhook to clean sessions |
| Extension not showing | Not deployed | Run shopify app deploy |
| Polaris styles missing | Missing provider | Wrap app in <AppProvider> |
# Fastest way to start — uses official template
shopify app init --template remix
# Or clone directly
npx degit Shopify/shopify-app-template-remix my-shopify-app
cd my-shopify-app
npm install
shopify app dev
For multi-environment setup, see shopify-multi-env-setup.