@webdevarif/shopify-core
v0.1.0
Published
Reusable Shopify app foundation — auth, billing, webhooks, DB helpers for React Router v7 + Drizzle ORM
Maintainers
Readme
@webdevarif/shopify-core
Reusable Shopify app foundation for React Router v7 + Drizzle ORM. Auth, billing, webhooks, database helpers, and encryption — all configurable via factory functions.
Install
npm install @webdevarif/shopify-corePeer dependencies:
npm install @shopify/shopify-app-react-router @shopify/shopify-api react-router drizzle-ormQuick Start
1. Create the Shopify core instance
// app/shopify.server.ts
import { createShopifyCore } from '@webdevarif/shopify-core'
import { sessionTable } from '@webdevarif/shopify-core/db'
import { DrizzleSessionStoragePostgres } from '@shopify/shopify-app-session-storage-drizzle'
import { db } from './db.server'
const core = createShopifyCore({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
appUrl: process.env.SHOPIFY_APP_URL!,
scopes: ['read_products', 'write_products'],
sessionStorage: new DrizzleSessionStoragePostgres(db, sessionTable),
billing: {
plans: [
{ key: 'FREE', name: 'Free', amount: 0, currencyCode: 'USD', interval: 'Every30Days' },
{ key: 'PRO', name: 'Pro', amount: 29, currencyCode: 'USD', interval: 'Every30Days', trialDays: 14 },
],
},
webhooks: {
handlers: {
APP_UNINSTALLED: async ({ shop }) => { /* cleanup */ },
APP_SUBSCRIPTIONS_UPDATE: async ({ shop, payload }) => { /* sync plan */ },
},
},
})
// Export raw instance + authenticate helpers
export default core
export const authenticate = core.authenticate2. Use in routes
// app/routes/app._index.tsx
import core from '../shopify.server'
export const loader = async ({ request }) => {
const { admin, session } = await core.authenticate.admin(request)
// Your app logic here
return { shop: session.shop }
}3. Set up billing
// app/billing.server.ts
import { createBillingModule } from '@webdevarif/shopify-core/billing'
import core from './shopify.server'
export const billing = createBillingModule({
core,
plans: [
{ key: 'FREE', name: 'Free', amount: 0, currencyCode: 'USD', interval: 'Every30Days' },
{ key: 'PRO', name: 'Pro', amount: 29, currencyCode: 'USD', interval: 'Every30Days', trialDays: 14 },
],
planSelectionPath: '/app/billing',
})
// In a protected route loader:
export const loader = async ({ request }) => {
const { subscription } = await billing.requireSubscription(request, ['PRO'])
// Only reaches here if merchant has an active PRO subscription
}4. Set up webhooks
// app/webhooks.server.ts
import { createWebhookHandlers } from '@webdevarif/shopify-core/webhooks'
import core from './shopify.server'
export const webhooks = createWebhookHandlers({
core,
handlers: {
APP_UNINSTALLED: async ({ shop }) => {
await shopHelpers.markUninstalled(shop)
},
APP_SUBSCRIPTIONS_UPDATE: async ({ shop, payload }) => {
await billing.handleSubscriptionWebhook({ shop, payload })
},
},
})
// app/routes/webhooks.app.uninstalled.tsx (thin route)
import { webhooks } from '../webhooks.server'
export const action = ({ request }) => webhooks.handle(request, 'APP_UNINSTALLED')5. Database helpers
import { createShopHelpers, shopsTable, sessionTable } from '@webdevarif/shopify-core/db'
const shopHelpers = createShopHelpers(db)
const shop = await shopHelpers.getShop('my-store.myshopify.com')
await shopHelpers.updatePlan('my-store.myshopify.com', 'pro', 'gid://...')6. Encryption
import { encrypt, decrypt } from '@webdevarif/shopify-core/encryption'
const encrypted = encrypt('ftp-password', process.env.ENCRYPTION_KEY!)
const decrypted = decrypt(encrypted, process.env.ENCRYPTION_KEY!)Entry Points
| Import | Contents |
|--------|----------|
| @webdevarif/shopify-core | createShopifyCore, types, GraphQL helpers |
| @webdevarif/shopify-core/billing | createBillingModule, billing types |
| @webdevarif/shopify-core/webhooks | createWebhookHandlers, webhook types |
| @webdevarif/shopify-core/db | Drizzle schemas, createDb, createShopHelpers |
| @webdevarif/shopify-core/encryption | encrypt, decrypt, isEncrypted |
License
MIT
