Skip to content
Back to skills

Node Backend

ASecurity

Build Node.js backends for Shopify apps — Remix server, @shopify/shopify-app-remix, session storage, OAuth handling, API proxy, webhook processing, and deployment. Use when building the server-side component of Shopify apps.

  • 39 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 7, 2026
ai-agentstypescriptbashsqlreactnodeawstestinggitapidatabase

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 7, 2026

npx -y skills add OrcaQubits/agentic-commerce-skills-plugins --skill node-backend --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Node Backend?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Node Backend
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/orcaqubits-node-backend-926c267b/badge)](https://www.skillsdirectory.com/skills/orcaqubits-node-backend-926c267b)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: node-backend
description: Build Node.js backends for Shopify apps — Remix server, @shopify/shopify-app-remix, session storage, OAuth handling, API proxy, webhook processing, and deployment. Use when building the server-side component of Shopify apps.
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch
---

# Node.js Backend for Shopify Apps

## Before writing code

**Fetch live docs**:
1. Web-search `site:shopify.dev app remix server` for Remix app server patterns
2. Web-search `site:github.com shopify shopify-app-js` for Shopify Node.js libraries
3. Web-search `site:shopify.dev session storage` for session management options

## App Server Architecture

### Typical Stack

```
Shopify Admin (iframe)
    ↓ App Bridge session token
Remix Server (@shopify/shopify-app-remix)
    ↓ GraphQL Admin API calls
Shopify APIs
    ↓ Webhooks
Your Webhook Handler
```

### Key Packages

| Package | Purpose |
|---------|---------|
| `@shopify/shopify-app-remix` | Remix integration (auth, session, billing, webhooks) |
| `@shopify/shopify-api` | Low-level Shopify API client |
| `@shopify/app-bridge-react` | App Bridge React components |
| `@shopify/polaris` | Admin UI components |

### Project Structure

```
shopify-app/
├── app/
│   ├── routes/
│   │   ├── app._index.tsx        # App dashboard
│   │   ├── app.products.tsx      # Products page
│   │   ├── auth.$.tsx            # OAuth callback handler
│   │   └── webhooks.tsx          # Webhook endpoint
│   ├── shopify.server.ts         # Shopify API client initialization
│   ├── db.server.ts              # Database connection
│   └── root.tsx                  # Root layout
├── prisma/
│   └── schema.prisma             # Database schema (session storage)
├── extensions/                   # App extensions (Functions, checkout UI)
├── shopify.app.toml              # App configuration
├── .env
└── package.json
```

## Shopify App Remix Setup

### Server Initialization

```typescript
// app/shopify.server.ts
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.SCOPES?.split(","),
  authPathPrefix: "/auth",
  sessionStorage: new PrismaSessionStorage(prisma),
  distribution: AppDistribution.AppStore,
  webhooks: {
    APP_UNINSTALLED: {
      deliveryMethod: "http",
      callbackUrl: "/webhooks",
    },
  },
  hooks: {
    afterAuth: async ({ session }) => {
      // Register webhooks after successful auth
      shopify.registerWebhooks({ session });
    },
  },
});

export default shopify;
export const apiVersion = shopify.apiVersion;
export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;
export const authenticate = shopify.authenticate;
export const registerWebhooks = shopify.registerWebhooks;
```

### Authentication in Loaders

```typescript
// app/routes/app._index.tsx
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export async function loader({ request }: LoaderFunctionArgs) {
  const { admin } = await authenticate.admin(request);

  const response = await admin.graphql(`
    query {
      products(first: 10) {
        edges {
          node {
            id
            title
          }
        }
      }
    }
  `);

  const { data } = await response.json();
  return json({ products: data.products.edges });
}
```

### Webhook Handler

```typescript
// app/routes/webhooks.tsx
import type { ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export async function action({ request }: ActionFunctionArgs) {
  const { topic, shop, payload } = await authenticate.webhook(request);

  switch (topic) {
    case "APP_UNINSTALLED":
      // Clean up shop data
      await deleteShopData(shop);
      break;
    case "CUSTOMERS_DATA_REQUEST":
      // Handle GDPR data request
      break;
    case "CUSTOMERS_REDACT":
      // Delete customer data
      break;
    case "SHOP_REDACT":
      // Delete all shop data
      break;
  }

  return new Response();
}
```

## Session Storage Options

| Storage | Package | Use Case |
|---------|---------|----------|
| Prisma | `@shopify/shopify-app-session-storage-prisma` | Production (SQL databases) |
| SQLite | `@shopify/shopify-app-session-storage-sqlite` | Development, small apps |
| Redis | `@shopify/shopify-app-session-storage-redis` | High-traffic apps |
| Memory | `@shopify/shopify-app-session-storage-memory` | Testing only |
| DynamoDB | `@shopify/shopify-app-session-storage-dynamodb` | AWS deployments |

## GraphQL Admin Client

The authenticated admin client handles:
- Automatic token management
- Rate limiting (retries on 429)
- API versioning
- Type-safe queries (with codegen)

```typescript
// Make a GraphQL call
const response = await admin.graphql(`
  mutation ProductCreate($input: ProductInput!) {
    productCreate(input: $input) {
      product { id title }
      userErrors { field message }
    }
  }
`, {
  variables: {
    input: { title: "New Product" },
  },
});
```

## REST Client (Legacy)

For endpoints not yet available in GraphQL:

```typescript
const response = await admin.rest.get({
  path: 'themes',
});
```

**Note:** REST is deprecated. Use GraphQL whenever possible.

## Deployment

### Shopify-Hosted (Spin)

For apps using `shopify app deploy`:
- Automatic hosting
- Managed infrastructure
- Environment variables in Shopify admin

### Self-Hosted Options

| Platform | Setup |
|----------|-------|
| Vercel | `vercel deploy` |
| Fly.io | `fly deploy` |
| Railway | `railway deploy` |
| Render | Git push |
| AWS (ECS/Lambda) | SAM/CDK |

### Environment Variables

```
SHOPIFY_API_KEY=your-api-key
SHOPIFY_API_SECRET=your-api-secret
SHOPIFY_APP_URL=https://your-app.example.com
SCOPES=read_products,write_products,read_orders
DATABASE_URL=file:./dev.db
```

## Best Practices

- Use `@shopify/shopify-app-remix` — do not build auth/session from scratch
- Use Prisma for session storage in production (not SQLite or memory)
- Authenticate every request with `authenticate.admin(request)`
- Register webhooks in `afterAuth` hook — they need valid access tokens
- Implement all GDPR webhooks in the webhook handler
- Use GraphQL over REST for all API calls
- Handle `userErrors` in GraphQL mutations — 200 status does not mean success
- Store environment variables securely — never commit `.env`
- Use `shopify app dev` for local development (handles tunneling and auth)

Fetch the Shopify Remix app documentation and @shopify/shopify-app-remix package docs for exact initialization, authentication, and deployment patterns before implementing.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…