@veyralabs/skills 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,322 @@
1
+ # App Architecture — shopify-dev Reference
2
+
3
+ Decisions and patterns for Shopify app structure, auth, billing, and production readiness.
4
+
5
+ ---
6
+
7
+ ## Tech Stack (2025+)
8
+
9
+ Shopify's recommended stack for new apps:
10
+
11
+ | Layer | Tech |
12
+ |-------|------|
13
+ | Framework | Remix (replaces Express) |
14
+ | UI | Polaris + App Bridge |
15
+ | ORM | Prisma (SQLite dev, Postgres prod) |
16
+ | Auth | OAuth client credentials (Dev Dashboard) |
17
+ | Hosting | Fly.io, Railway, Vercel, Render |
18
+ | Extensions | Checkout UI, Admin UI, Web Pixel |
19
+
20
+ ---
21
+
22
+ ## Project Structure
23
+
24
+ ```
25
+ my-shopify-app/
26
+ ├── app/
27
+ │ ├── routes/
28
+ │ │ ├── app._index.tsx ← main embedded page
29
+ │ │ ├── app.products.tsx ← products feature page
30
+ │ │ ├── app.settings.tsx ← settings
31
+ │ │ ├── webhooks.tsx ← webhook receiver
32
+ │ │ └── auth.$.tsx ← Shopify OAuth flow (auto-generated)
33
+ │ ├── shopify.server.ts ← auth config, API client
34
+ │ ├── db.server.ts ← Prisma client singleton
35
+ │ └── root.tsx ← App wrapper with AppProvider
36
+ ├── extensions/
37
+ │ └── checkout-ui/ ← checkout extension (if any)
38
+ ├── prisma/
39
+ │ ├── schema.prisma
40
+ │ └── migrations/
41
+ ├── .env ← never commit
42
+ ├── shopify.app.toml ← app config for CLI
43
+ └── remix.config.js
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Authentication
49
+
50
+ ### How it works (OAuth client credentials, 2025+)
51
+
52
+ 1. Merchant installs app from App Store / Dev Dashboard
53
+ 2. Shopify redirects to your `/auth` route with shop domain
54
+ 3. Your app redirects to Shopify OAuth screen
55
+ 4. Merchant approves — Shopify redirects back with authorization code
56
+ 5. Your app exchanges code for session token
57
+ 6. Session stored in DB (per-shop)
58
+
59
+ All this is handled by `@shopify/shopify-app-remix` — you don't implement it manually.
60
+
61
+ ### shopify.server.ts pattern
62
+
63
+ ```typescript
64
+ import "@shopify/shopify-app-remix/adapters/node";
65
+ import {
66
+ AppDistribution,
67
+ shopifyApp,
68
+ LATEST_API_VERSION,
69
+ } from "@shopify/shopify-app-remix/server";
70
+ import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
71
+ import { PrismaClient } from "@prisma/client";
72
+
73
+ const prisma = new PrismaClient();
74
+
75
+ const shopify = shopifyApp({
76
+ apiKey: process.env.SHOPIFY_API_KEY,
77
+ apiSecretKey: process.env.SHOPIFY_API_SECRET || "",
78
+ apiVersion: LATEST_API_VERSION,
79
+ scopes: process.env.SCOPES?.split(","),
80
+ appUrl: process.env.SHOPIFY_APP_URL || "",
81
+ authPathPrefix: "/auth",
82
+ sessionStorage: new PrismaSessionStorage(prisma),
83
+ distribution: AppDistribution.AppStore,
84
+ });
85
+
86
+ export default shopify;
87
+ export const apiVersion = LATEST_API_VERSION;
88
+ export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;
89
+ export const authenticate = shopify.authenticate;
90
+ export const unauthenticated = shopify.unauthenticated;
91
+ export const login = shopify.login;
92
+ export const registerWebhooks = shopify.registerWebhooks;
93
+ export const sessionStorage = shopify.sessionStorage;
94
+ ```
95
+
96
+ ### Using auth in routes
97
+
98
+ ```typescript
99
+ // Embedded page (inside Shopify admin)
100
+ export const loader = async ({ request }) => {
101
+ const { admin, session } = await authenticate.admin(request);
102
+ // admin.graphql() for API calls
103
+ // session.shop = "store-name.myshopify.com"
104
+ };
105
+
106
+ // Webhook (not embedded — different auth method)
107
+ export const action = async ({ request }) => {
108
+ const { shop, topic, payload } = await authenticate.webhook(request);
109
+ };
110
+
111
+ // Public (no auth — for storefront-facing APIs)
112
+ export const loader = async ({ request }) => {
113
+ const { storefront } = await unauthenticated.storefront("store.myshopify.com");
114
+ };
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Database
120
+
121
+ ### Prisma schema for session storage
122
+
123
+ ```prisma
124
+ model Session {
125
+ id String @id
126
+ shop String
127
+ state String
128
+ isOnline Boolean @default(false)
129
+ scope String?
130
+ expires DateTime?
131
+ accessToken String
132
+ userId BigInt?
133
+ firstName String?
134
+ lastName String?
135
+ email String?
136
+ accountOwner Boolean @default(false)
137
+ locale String?
138
+ collaborator Boolean? @default(false)
139
+ emailVerified Boolean? @default(false)
140
+ }
141
+ ```
142
+
143
+ ### App data example (products table)
144
+
145
+ ```prisma
146
+ model ProductSetting {
147
+ id String @id @default(uuid())
148
+ shop String
149
+ productId String
150
+ badgeText String?
151
+ isHidden Boolean @default(false)
152
+ createdAt DateTime @default(now())
153
+ updatedAt DateTime @updatedAt
154
+
155
+ @@unique([shop, productId])
156
+ }
157
+ ```
158
+
159
+ ### Dev vs prod DB
160
+
161
+ - Dev: SQLite (zero setup, file-based)
162
+ - Prod: Postgres (Railway, Supabase, PlanetScale)
163
+
164
+ Switch in `prisma/schema.prisma`:
165
+ ```prisma
166
+ datasource db {
167
+ provider = "postgresql" // "sqlite" for dev
168
+ url = env("DATABASE_URL")
169
+ }
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Billing
175
+
176
+ Shopify billing API requires app to charge via Shopify (mandatory for App Store apps).
177
+
178
+ ### Create a subscription
179
+
180
+ ```typescript
181
+ export const loader = async ({ request }) => {
182
+ const { billing } = await authenticate.admin(request);
183
+
184
+ const { hasActivePayment, appSubscriptions } = await billing.check({
185
+ plans: ["Basic Plan"],
186
+ isTest: process.env.NODE_ENV !== "production",
187
+ });
188
+
189
+ if (!hasActivePayment) {
190
+ await billing.request({
191
+ plan: "Basic Plan",
192
+ isTest: process.env.NODE_ENV !== "production",
193
+ returnUrl: `${process.env.SHOPIFY_APP_URL}/app`,
194
+ });
195
+ }
196
+ };
197
+ ```
198
+
199
+ ### Define plans in shopify.server.ts
200
+
201
+ ```typescript
202
+ const shopify = shopifyApp({
203
+ // ...existing config...
204
+ billing: {
205
+ "Basic Plan": {
206
+ amount: 9.99,
207
+ currencyCode: "USD",
208
+ interval: BillingInterval.Monthly,
209
+ },
210
+ "Pro Plan": {
211
+ amount: 29.99,
212
+ currencyCode: "USD",
213
+ interval: BillingInterval.Monthly,
214
+ },
215
+ },
216
+ });
217
+ ```
218
+
219
+ ### Check subscription status in any route
220
+
221
+ ```typescript
222
+ const { billing } = await authenticate.admin(request);
223
+ const { hasActivePayment } = await billing.check({ plans: ["Pro Plan"] });
224
+
225
+ if (!hasActivePayment) {
226
+ return redirect("/app/upgrade");
227
+ }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Webhooks
233
+
234
+ ### Register in shopify.app.toml
235
+
236
+ ```toml
237
+ [[webhooks.subscriptions]]
238
+ topics = ["products/update", "products/delete"]
239
+ uri = "/webhooks"
240
+
241
+ [[webhooks.subscriptions]]
242
+ topics = ["app/uninstalled"]
243
+ uri = "/webhooks"
244
+ ```
245
+
246
+ Or register programmatically:
247
+
248
+ ```typescript
249
+ // root.tsx or entry point
250
+ import { registerWebhooks } from "./shopify.server";
251
+
252
+ export const action = async ({ request }) => {
253
+ const { topic, shop } = await authenticate.webhook(request);
254
+ await registerWebhooks({ session });
255
+ };
256
+ ```
257
+
258
+ ### Webhook handler
259
+
260
+ ```typescript
261
+ // app/routes/webhooks.tsx
262
+ export const action = async ({ request }) => {
263
+ const { topic, shop, session, admin, payload } =
264
+ await authenticate.webhook(request);
265
+
266
+ if (!admin && topic !== "APP_UNINSTALLED") {
267
+ throw new Response();
268
+ }
269
+
270
+ switch (topic) {
271
+ case "APP_UNINSTALLED":
272
+ if (session) {
273
+ await db.session.deleteMany({ where: { shop } });
274
+ }
275
+ break;
276
+
277
+ case "PRODUCTS_UPDATE":
278
+ await syncProduct(shop, payload);
279
+ break;
280
+
281
+ default:
282
+ throw new Response("Unhandled webhook topic", { status: 404 });
283
+ }
284
+
285
+ return new Response();
286
+ };
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Production Checklist
292
+
293
+ Before submitting to App Store:
294
+
295
+ - [ ] `LATEST_API_VERSION` set and updated quarterly
296
+ - [ ] Webhooks registered for `APP_UNINSTALLED`, `CUSTOMERS_DATA_REQUEST`, `CUSTOMERS_REDACT`, `SHOP_REDACT` (GDPR — mandatory)
297
+ - [ ] Billing implemented if charging merchants
298
+ - [ ] Session storage uses Postgres (not SQLite)
299
+ - [ ] All Admin API calls handle `userErrors`
300
+ - [ ] Rate limit handling: retry on 429 with exponential backoff
301
+ - [ ] App URL set in `.env` and `shopify.app.toml`
302
+ - [ ] Polaris `AppProvider` wraps entire app with i18n translations
303
+ - [ ] No API keys or secrets in client-side code
304
+ - [ ] App passes Shopify's built-in review checklist (run `shopify app build` and check output)
305
+
306
+ ---
307
+
308
+ ## Rate Limits
309
+
310
+ Shopify Admin GraphQL: 1000 points/second per store.
311
+
312
+ Query cost: each field costs 1 point; connections multiply by requested count.
313
+ `products(first: 250)` = ~250 points. `products.variants.metafields` = ~250 × 250.
314
+
315
+ ```typescript
316
+ // Check cost in response headers
317
+ const response = await admin.graphql(QUERY);
318
+ const headers = response.headers;
319
+ // X-Shopify-Shop-Api-Call-Limit: used/max
320
+ ```
321
+
322
+ For bulk operations (catalog exports, large migrations), use `bulkOperationRunQuery` — no rate limits.
@@ -0,0 +1,257 @@
1
+ # CLI Workflows — shopify-dev Reference
2
+
3
+ Shopify CLI commands for theme and app development. Verified against CLI 3.x.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g @shopify/cli@latest
11
+ shopify version
12
+ ```
13
+
14
+ Requires Node 18+. Authentication is store-scoped (you authenticate per store).
15
+
16
+ ---
17
+
18
+ ## Theme Development
19
+
20
+ ### Basic workflow
21
+
22
+ ```bash
23
+ # Authenticate and pull from live theme
24
+ shopify theme dev --store your-store.myshopify.com
25
+
26
+ # Start dev server on specific theme
27
+ shopify theme dev --store your-store.myshopify.com --theme THEME_ID
28
+
29
+ # Check what themes exist
30
+ shopify theme list --store your-store.myshopify.com
31
+
32
+ # Pull theme files to local
33
+ shopify theme pull --store your-store.myshopify.com --theme THEME_ID
34
+
35
+ # Push to a specific theme (NOT live — always push to unpublished theme first)
36
+ shopify theme push --store your-store.myshopify.com --theme THEME_ID
37
+
38
+ # Push to new theme (creates it)
39
+ shopify theme push --unpublished --store your-store.myshopify.com
40
+
41
+ # Publish a theme (makes it live)
42
+ shopify theme publish --store your-store.myshopify.com --theme THEME_ID
43
+ ```
44
+
45
+ ### Targeting specific files
46
+
47
+ ```bash
48
+ # Push only changed files
49
+ shopify theme push --only sections/header.liquid
50
+
51
+ # Ignore files during push/pull
52
+ shopify theme push --ignore "config/settings_data.json"
53
+ shopify theme pull --ignore "templates/*.json"
54
+ ```
55
+
56
+ ### Multiple environments
57
+
58
+ Define in `shopify.theme.toml`:
59
+
60
+ ```toml
61
+ [environments.development]
62
+ store = "dev-store.myshopify.com"
63
+ theme = "123456789"
64
+ ignore = ["config/settings_data.json"]
65
+
66
+ [environments.staging]
67
+ store = "staging-store.myshopify.com"
68
+ theme = "987654321"
69
+
70
+ [environments.production]
71
+ store = "prod-store.myshopify.com"
72
+ theme = "111111111"
73
+ ```
74
+
75
+ Then:
76
+ ```bash
77
+ shopify theme dev --environment development
78
+ shopify theme push --environment staging
79
+ shopify theme push --environment production
80
+ ```
81
+
82
+ ### Check for errors
83
+
84
+ ```bash
85
+ shopify theme check
86
+ shopify theme check --output json
87
+
88
+ # Check specific files
89
+ shopify theme check sections/product-form.liquid
90
+ ```
91
+
92
+ `theme check` validates Liquid syntax, section schemas, deprecated filters, accessibility issues.
93
+
94
+ ---
95
+
96
+ ## App Development
97
+
98
+ ### Scaffold a new app
99
+
100
+ ```bash
101
+ npm init @shopify/app@latest
102
+ # Prompts: app name, template (Remix recommended), language (TypeScript or JS)
103
+ cd my-app
104
+ npm install
105
+ ```
106
+
107
+ ### Start dev server
108
+
109
+ ```bash
110
+ shopify app dev
111
+ # Starts: local server + tunnel (Cloudflare ngrok) + updates app URLs in Dev Dashboard
112
+ ```
113
+
114
+ Dev server requirements:
115
+ - Authenticated Shopify Partner/Dev Dashboard account
116
+ - App created in Dev Dashboard (first run prompts you to create)
117
+ - `.shopify/project.toml` created automatically on first `dev`
118
+
119
+ ### Environment config
120
+
121
+ `.env` file (auto-created, never commit):
122
+ ```
123
+ SHOPIFY_API_KEY=your-client-id
124
+ SHOPIFY_API_SECRET=your-client-secret
125
+ SCOPES=write_products,read_orders
126
+ ```
127
+
128
+ ### Deploy to production
129
+
130
+ ```bash
131
+ shopify app deploy
132
+ # Builds, runs type check, deploys extensions to Shopify
133
+ # Does NOT deploy the web server — deploy that to Vercel/Railway/etc.
134
+ ```
135
+
136
+ ### Generate extension scaffold
137
+
138
+ ```bash
139
+ # Checkout UI extension
140
+ shopify app generate extension --type checkout_ui_extension
141
+
142
+ # Product subscription
143
+ shopify app generate extension --type product_subscription
144
+
145
+ # Admin action
146
+ shopify app generate extension --type admin_action
147
+
148
+ # Web pixel
149
+ shopify app generate extension --type web_pixel_extension
150
+ ```
151
+
152
+ ### App info
153
+
154
+ ```bash
155
+ shopify app info
156
+ # Shows: app name, client ID, extensions, deployment status
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Authentication
162
+
163
+ ```bash
164
+ # Login to a store
165
+ shopify auth login --store your-store.myshopify.com
166
+
167
+ # Check current auth status
168
+ shopify auth status
169
+
170
+ # Logout
171
+ shopify auth logout
172
+ ```
173
+
174
+ First `shopify app dev` or `shopify theme dev` on a new machine prompts for auth.
175
+
176
+ ---
177
+
178
+ ## Useful Flags
179
+
180
+ | Flag | Command | What it does |
181
+ |------|---------|-------------|
182
+ | `--store` | theme | Target store domain |
183
+ | `--theme THEME_ID` | theme | Target specific theme |
184
+ | `--environment` | theme | Use environment from .toml |
185
+ | `--only file.liquid` | theme push/pull | Scope to specific files |
186
+ | `--ignore "pattern"` | theme push/pull | Exclude files |
187
+ | `--unpublished` | theme push | Create new unpublished theme |
188
+ | `--live` | theme push | Push to currently active theme |
189
+ | `--json` | most commands | Machine-readable output |
190
+ | `--verbose` | most commands | Debug output |
191
+
192
+ ---
193
+
194
+ ## CI/CD Pattern
195
+
196
+ ### GitHub Actions — theme deploy
197
+
198
+ ```yaml
199
+ name: Deploy Theme
200
+
201
+ on:
202
+ push:
203
+ branches: [main]
204
+
205
+ jobs:
206
+ deploy:
207
+ runs-on: ubuntu-latest
208
+ steps:
209
+ - uses: actions/checkout@v3
210
+
211
+ - name: Setup Node
212
+ uses: actions/setup-node@v3
213
+ with:
214
+ node-version: 18
215
+
216
+ - name: Install Shopify CLI
217
+ run: npm install -g @shopify/cli@latest
218
+
219
+ - name: Deploy to staging
220
+ env:
221
+ SHOPIFY_CLI_THEME_TOKEN: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
222
+ run: |
223
+ shopify theme push \
224
+ --store ${{ secrets.SHOPIFY_STORE }} \
225
+ --theme ${{ secrets.STAGING_THEME_ID }} \
226
+ --allow-live \
227
+ --no-color
228
+ ```
229
+
230
+ Generate `SHOPIFY_CLI_THEME_TOKEN`:
231
+ 1. Shopify Admin → Settings → Apps and sales channels → Develop apps
232
+ 2. Create app with `write_themes` scope
233
+ 3. Get Admin API access token
234
+
235
+ ### GitHub Actions — app deploy extensions
236
+
237
+ ```yaml
238
+ - name: Deploy app extensions
239
+ env:
240
+ SHOPIFY_API_KEY: ${{ secrets.SHOPIFY_API_KEY }}
241
+ SHOPIFY_API_SECRET: ${{ secrets.SHOPIFY_API_SECRET }}
242
+ run: shopify app deploy --no-release
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Common Errors
248
+
249
+ **`Error: No store provided`** — add `--store` flag or set in `shopify.theme.toml`
250
+
251
+ **`Error: Theme not found`** — wrong THEME_ID. Run `shopify theme list` to get correct ID.
252
+
253
+ **`Error: Cannot push to live theme`** — use `--allow-live` flag explicitly, or push to unpublished theme first.
254
+
255
+ **`Error: SHOPIFY_CLI_THEME_TOKEN not set`** — CI/CD needs token. See token generation above.
256
+
257
+ **`Error: Extensions failed to deploy`** — check extension `shopify.extension.toml` for schema errors. Run `shopify app generate extension` again if structure is corrupted.