Help us improve
Share bugs, ideas, or general feedback.
From bigcommerce-commerce
Builds Node.js backends for BigCommerce apps using Express/Fastify, with OAuth callbacks, JWT/webhook verification, API proxying, session management, and deployment setup.
npx claudepluginhub orcaqubits/agentic-commerce-skills-plugins --plugin bigcommerce-commerceHow this skill is triggered — by the user, by Claude, or both
Slash command
/bigcommerce-commerce:node-backendThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Fetch live docs**:
Builds Node.js backends for Shopify apps using Remix and @shopify/shopify-app-remix for OAuth, Prisma sessions, API proxying, webhooks, and deployment.
Builds BigCommerce apps: single-click OAuth installs, load/uninstall callbacks, connector apps, Script Manager, and App Marketplace submission.
Installs and configures Shopify app authentication with OAuth, session tokens, and @shopify/shopify-api SDK for API access in Node.js apps.
Share bugs, ideas, or general feedback.
Fetch live docs:
site:developer.bigcommerce.com apps guide for app development patternshttps://expressjs.com/ or https://fastify.dev/ for framework docsbigcommerce node sample app github for official sample appsBigCommerce Admin (iframe)
↓ OAuth flow / Load callback
Your Node.js Server (Express/Fastify)
↓ API calls
BigCommerce REST/GraphQL APIs
↓ Webhooks
Your Webhook Handler
bc-app/
├── src/
│ ├── index.ts # Server entry point
│ ├── routes/
│ │ ├── auth.ts # OAuth callbacks (install, load, uninstall)
│ │ ├── api.ts # App API routes
│ │ └── webhooks.ts # Webhook handlers
│ ├── services/
│ │ ├── bigcommerce.ts # BigCommerce API client
│ │ └── store.ts # Store/token management
│ ├── middleware/
│ │ ├── auth.ts # Authentication middleware
│ │ └── verify.ts # JWT/webhook verification
│ └── lib/
│ ├── jwt.ts # JWT utilities
│ └── db.ts # Database connection
├── .env # Environment variables
├── package.json
└── tsconfig.json
// routes/auth.ts
app.get('/auth/install', async (req, res) => {
const { code, scope, context } = req.query;
// Exchange code for permanent token
const tokenResponse = await fetch('https://login.bigcommerce.com/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
client_id: process.env.BC_CLIENT_ID,
client_secret: process.env.BC_CLIENT_SECRET,
code,
scope,
grant_type: 'authorization_code',
redirect_uri: process.env.BC_AUTH_CALLBACK,
context,
}),
});
const { access_token, user, context: storeContext } = await tokenResponse.json();
const storeHash = storeContext.split('/')[1];
// Store token securely
await saveStoreToken(storeHash, access_token, user);
// Return app UI
res.send('<html>App installed successfully!</html>');
});
app.get('/auth/load', async (req, res) => {
const signedPayload = req.query.signed_payload_jwt as string;
// Verify JWT
const decoded = verifyJwt(signedPayload, process.env.BC_CLIENT_SECRET!);
const storeHash = decoded.sub.split('/')[1];
const userId = decoded.user.id;
// Load store token
const token = await getStoreToken(storeHash);
// Render app UI
res.send(renderApp(storeHash, userId));
});
app.get('/auth/uninstall', async (req, res) => {
const signedPayload = req.query.signed_payload_jwt as string;
const decoded = verifyJwt(signedPayload, process.env.BC_CLIENT_SECRET!);
const storeHash = decoded.sub.split('/')[1];
// Clean up stored data
await deleteStoreData(storeHash);
res.status(200).send('OK');
});
import jwt from 'jsonwebtoken';
function verifyJwt(token: string, secret: string) {
return jwt.verify(token, secret, {
algorithms: ['HS256'],
audience: process.env.BC_CLIENT_ID,
});
}
class BigCommerceClient {
constructor(
private storeHash: string,
private accessToken: string,
) {}
private async request<T>(path: string, options?: RequestInit): Promise<T> {
const url = `https://api.bigcommerce.com/stores/${this.storeHash}${path}`;
const response = await fetch(url, {
...options,
headers: {
'X-Auth-Token': this.accessToken,
'Content-Type': 'application/json',
Accept: 'application/json',
...options?.headers,
},
});
if (response.status === 429) {
const retryAfter = response.headers.get('X-Rate-Limit-Time-Reset-Ms');
// Implement backoff
}
if (!response.ok) {
throw new ApiError(response.status, await response.text());
}
return response.json();
}
async getProducts(params?: Record<string, string>) {
const query = new URLSearchParams(params).toString();
return this.request(`/v3/catalog/products?${query}`);
}
async getOrder(orderId: number) {
return this.request(`/v2/orders/${orderId}`);
}
}
app.post('/webhooks/orders', async (req, res) => {
// Verify webhook (check custom header)
const secret = req.headers['x-webhook-secret'];
if (secret !== process.env.WEBHOOK_SECRET) {
return res.status(401).send('Unauthorized');
}
// Acknowledge immediately
res.status(200).send('OK');
// Process asynchronously
const { scope, data, store_id } = req.body;
await processOrderEvent(store_id, data.id, scope);
});
Store sessions with store hash context:
storeHash and userIdapp.use(session({
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JS access
sameSite: 'none', // Required for iframe embedding
maxAge: 24 * 60 * 60 * 1000,
},
}));
Note: sameSite: 'none' is required because BigCommerce apps load in an iframe.
// Global error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error('Unhandled error:', err);
res.status(500).json({ error: 'Internal server error' });
});
// API error class
class ApiError extends Error {
constructor(public statusCode: number, message: string) {
super(message);
}
}
| Platform | Pros | Setup |
|---|---|---|
| Vercel | Easy, auto-scaling, edge functions | vercel deploy |
| Railway | Simple, DB support | railway deploy |
| Render | Free tier, managed services | Git push |
| AWS Lambda | Serverless, pay-per-use | SAM/CDK |
| Heroku | Classic PaaS | git push heroku |
Always set via platform's secret management — never in code:
BC_CLIENT_ID=xxx
BC_CLIENT_SECRET=xxx
BC_AUTH_CALLBACK=https://your-app.com/auth/install
BC_LOAD_CALLBACK=https://your-app.com/auth/load
BC_UNINSTALL_CALLBACK=https://your-app.com/auth/uninstall
SESSION_SECRET=xxx
WEBHOOK_SECRET=xxx
DATABASE_URL=xxx
sameSite: 'none' + secure: true cookies for iframe embeddingFetch the BigCommerce app development guide and Node.js framework docs for exact callback parameters, JWT structure, and deployment patterns before implementing.