Help us improve
Share bugs, ideas, or general feedback.
From shopify-pack
Installs and configures Shopify app authentication with OAuth, session tokens, and @shopify/shopify-api SDK for API access in Node.js apps.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin shopify-packHow this skill is triggered — by the user, by Claude, or both
Slash command
/shopify-pack:shopify-install-authThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Set up Shopify app authentication using the official `@shopify/shopify-api` library. Covers OAuth flow, session token exchange, custom app tokens, and Storefront API access.
Sets up Shopify CLI auth and Admin API access token for a store: install CLI, login, create custom app with scopes, store token securely, verify with GraphQL. For store connections or auth issues.
Guides Shopify app development with Remix template: app types, OAuth flow, session tokens, App Bridge, webhooks, extensions, and embedded admin apps.
Creates minimal Shopify app querying products and store info via GraphQL Admin API. Use for new integrations, setup testing, or learning API patterns.
Share bugs, ideas, or general feedback.
Set up Shopify app authentication using the official @shopify/shopify-api library. Covers OAuth flow, session token exchange, custom app tokens, and Storefront API access.
@shopify/shopify-api v9+ requires it)# Core library + Node.js runtime adapter
npm install @shopify/shopify-api @shopify/shopify-app-remix
# Or for standalone Node apps:
npm install @shopify/shopify-api @shopify/shopify-app-express
# For Remix (recommended by Shopify):
npm install @shopify/shopify-app-remix @shopify/app-bridge-react
Create a .env file (add to .gitignore immediately):
# .env — NEVER commit this file
SHOPIFY_API_KEY=your_app_api_key
SHOPIFY_API_SECRET=your_app_api_secret
SHOPIFY_SCOPES=read_products,write_products,read_orders,write_orders
SHOPIFY_APP_URL=https://your-app.example.com
SHOPIFY_HOST_NAME=your-app.example.com
# For custom/private apps only:
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxxx
# API version — use a stable quarterly release
SHOPIFY_API_VERSION=2024-10
# .gitignore — add these immediately
.env
.env.local
.env.*.local
// src/shopify.ts
import "@shopify/shopify-api/adapters/node";
import { shopifyApi, LATEST_API_VERSION, Session } from "@shopify/shopify-api";
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
scopes: process.env.SHOPIFY_SCOPES!.split(","),
hostName: process.env.SHOPIFY_HOST_NAME!,
apiVersion: LATEST_API_VERSION, // or "2024-10"
isEmbeddedApp: true,
});
export default shopify;
// routes/auth.ts — Express example
import express from "express";
import shopify from "../shopify";
const router = express.Router();
// Step 1: Begin OAuth — redirect merchant to Shopify
router.get("/auth", async (req, res) => {
const shop = req.query.shop as string;
// Generates the authorization URL with HMAC validation
const authRoute = await shopify.auth.begin({
shop: shopify.utils.sanitizeShop(shop, true)!,
callbackPath: "/auth/callback",
isOnline: false, // offline = long-lived token
rawRequest: req,
rawResponse: res,
});
});
// Step 2: Handle callback — exchange code for access token
router.get("/auth/callback", async (req, res) => {
const callback = await shopify.auth.callback({
rawRequest: req,
rawResponse: res,
});
// callback.session contains the access token
const session: Session = callback.session;
console.log("Access token:", session.accessToken);
console.log("Shop:", session.shop); // "store-name.myshopify.com"
console.log("Scopes:", session.scope); // "read_products,write_products,..."
// IMPORTANT: Persist session to your database
await saveSession(session);
res.redirect(`/?shop=${session.shop}&host=${req.query.host}`);
});
export default router;
For embedded apps, use session token exchange instead of traditional OAuth:
// Token exchange — converts session token (JWT) to API access token
import shopify from "../shopify";
async function exchangeToken(
shop: string,
sessionToken: string
): Promise<Session> {
const { session } = await shopify.auth.tokenExchange({
sessionToken,
shop,
requestedTokenType: RequestedTokenType.OfflineAccessToken,
});
return session;
}
For custom apps (installed on a single store), use a permanent access token:
// Custom app — no OAuth needed, use Admin API access token directly
import "@shopify/shopify-api/adapters/node";
import { shopifyApi } from "@shopify/shopify-api";
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
hostName: process.env.SHOPIFY_HOST_NAME!,
apiVersion: "2024-10",
isCustomStoreApp: true,
adminApiAccessToken: process.env.SHOPIFY_ACCESS_TOKEN!, // shpat_xxx
});
// Create a session for the custom app
const session = shopify.session.customAppSession(
"your-store.myshopify.com"
);
// Use GraphQL client directly
const client = new shopify.clients.Graphql({ session });
// Quick connectivity test
async function verifyShopifyAuth(session: Session): Promise<void> {
const client = new shopify.clients.Graphql({ session });
const response = await client.request(`{
shop {
name
email
plan {
displayName
}
primaryDomain {
url
}
}
}`);
console.log("Connected to:", response.data.shop.name);
console.log("Plan:", response.data.shop.plan.displayName);
console.log("Domain:", response.data.shop.primaryDomain.url);
}
@shopify/shopify-api installed and configured| Error | Cause | Solution |
|---|---|---|
InvalidApiKeyError | Wrong SHOPIFY_API_KEY | Verify in Partner Dashboard > App > API credentials |
InvalidHmacError during callback | Secret mismatch or URL tampering | Check SHOPIFY_API_SECRET matches Partner Dashboard |
SessionNotFound | Session not persisted | Implement SessionStorage (DB, Redis, or file) |
HttpResponseError: 401 | Token expired or revoked | Merchant uninstalled app — trigger re-auth |
InvalidScopeError | Requested scope not approved | Only use scopes from the approved list in your app config |
ShopifyErrors.InvalidShop | Malformed shop domain | Must be *.myshopify.com — use sanitizeShop() |
| Scope | Grants Access To |
|---|---|
read_products / write_products | Products, variants, collections, images |
read_orders / write_orders | Orders, transactions, fulfillments |
read_customers / write_customers | Customer data, addresses, metafields |
read_inventory / write_inventory | Inventory levels across locations |
read_content / write_content | Pages, blogs, articles |
read_themes / write_themes | Theme files and assets |
read_shipping / write_shipping | Shipping zones, carrier services |
read_fulfillments / write_fulfillments | Fulfillment orders and services |
// Storefront API uses a different token — generated in admin
const storefrontClient = new shopify.clients.Storefront({
session,
apiVersion: "2024-10",
});
const products = await storefrontClient.request(`{
products(first: 10) {
edges {
node {
id
title
handle
priceRange {
minVariantPrice {
amount
currencyCode
}
}
}
}
}
}`);
After successful auth, proceed to shopify-hello-world for your first API call.