Skip to content

diegolarraz/shopify-next-supabase-starter

Repository files navigation

Shopify Next.js Supabase Starter πŸš€

A production-ready Shopify embedded app template built with Next.js 14+ App Router, TypeScript, Supabase, and TanStack Query. This starter follows all security best practices and provides a scalable architecture for building modern Shopify apps.

Next.js TypeScript Shopify Supabase TanStack Query

🌟 Features

  • βœ… Shopify App Bridge v4 with secure token exchange authentication
  • βœ… Supabase for session/install storage (no Prisma, no Docker)
  • βœ… OOP Repository Pattern + Service Singleton for all external calls
  • βœ… TanStack Query hooks for client data fetching (no direct fetch in components)
  • βœ… Security-first: Strict CSP, no token logging, proper auth error handling
  • βœ… Webhooks: Server-side registration, APP_UNINSTALLED cleanup, GDPR handlers
  • βœ… GraphQL Codegen for type-safe operations
  • βœ… TypeScript throughout with full type safety
  • βœ… Shopify Polaris UI components
  • βœ… Production-ready build and deployment configuration

πŸ“¦ Dependencies

Category Package Version Purpose
Core Framework next 15.5.2 Next.js App Router framework
react ^18.3.1 React library (v18 for Polaris compatibility)
react-dom ^18.3.1 React DOM rendering
typescript ^5 TypeScript support
Shopify Integration @shopify/shopify-api ^11.0.0 Shopify API SDK with webhooks & auth
@shopify/app-bridge-react ^4.1.3 App Bridge v4 React components
@shopify/polaris ^12.0.0 Shopify's design system components
Database & Backend @supabase/supabase-js ^2.39.7 Supabase client for database operations
State Management @tanstack/react-query ^5.28.6 Server state management & caching
GraphQL graphql ^16.8.1 GraphQL core library
graphql-request ^6.1.0 Lightweight GraphQL client
@graphql-codegen/cli ^5.0.2 GraphQL code generation CLI
@graphql-codegen/client-preset ^4.2.4 Client-side code generation preset
@graphql-typed-document-node/core ^3.2.0 TypeScript support for GraphQL documents

πŸ—οΈ Project Architecture

shopify-next-supabase-starter/
β”œβ”€β”€ πŸ“ app/
β”‚   β”œβ”€β”€ πŸ“ api/
β”‚   β”‚   β”œβ”€β”€ πŸ“ hello/
β”‚   β”‚   β”‚   └── route.ts              # Example authenticated API route
β”‚   β”‚   └── πŸ“ webhooks/
β”‚   β”‚       └── route.ts              # Shopify webhook handler
β”‚   β”œβ”€β”€ πŸ“ hooks/
β”‚   β”‚   β”œβ”€β”€ useGraphQL.ts             # Generic type-safe GraphQL hook
β”‚   β”‚   └── useSession.ts             # Session management hook
β”‚   β”œβ”€β”€ πŸ“ providers/
β”‚   β”‚   β”œβ”€β”€ providers.tsx             # Main providers wrapper
β”‚   β”‚   β”œβ”€β”€ query-client.tsx          # TanStack Query provider
β”‚   β”‚   └── session-provider.tsx      # Session management provider
β”‚   β”œβ”€β”€ layout.tsx                    # Root layout with Shopify metadata
β”‚   β”œβ”€β”€ page.tsx                      # Main embedded app page
β”‚   └── globals.css                   # Global styles
β”œβ”€β”€ πŸ“ lib/
β”‚   β”œβ”€β”€ πŸ“ db/
β”‚   β”‚   β”œβ”€β”€ base-repository.ts        # Base repository class
β”‚   β”‚   β”œβ”€β”€ session.repository.ts     # Session data repository
β”‚   β”‚   β”œβ”€β”€ app-installation.repository.ts # App installation tracking
β”‚   β”‚   └── service.ts                # Database service singleton
β”‚   β”œβ”€β”€ πŸ“ shopify/
β”‚   β”‚   β”œβ”€β”€ initialize-context.ts     # Shopify API initialization
β”‚   β”‚   β”œβ”€β”€ verify.ts                 # Token verification utilities
β”‚   β”‚   β”œβ”€β”€ gdpr.ts                   # GDPR webhook handlers
β”‚   β”‚   └── register-webhooks.ts      # Webhook registration
β”‚   └── πŸ“ supabase/
β”‚       └── server.ts                 # Supabase admin client
β”œβ”€β”€ πŸ“ lib/gql/                       # Generated GraphQL types (auto-generated)
β”œβ”€β”€ codegen.ts                        # GraphQL Codegen configuration
β”œβ”€β”€ next.config.ts                    # Next.js config with CSP headers
β”œβ”€β”€ .env.example                      # Environment variables template
β”œβ”€β”€ shopify.app.example.toml          # Shopify CLI config template
└── SETUP.md                          # Detailed setup instructions

πŸš€ Quick Start

Prerequisites

Before you begin, ensure you have:

1. Clone and Install

git clone https://github.com/diegolarraz/shopify-next-supabase-starter
cd shopify-next-supabase-starter
npm install

2. Environment Setup

# Copy environment templates
cp .env.example .env.local
cp shopify.app.example.toml shopify.app.toml

πŸ”§ Shopify Account Setup

Step 1: Create a Shopify App

  1. Go to Shopify Partners Dashboard

  2. Create a New App

    • Click "Apps" in the sidebar
    • Click "Create app"
    • Choose "Create app manually"
    • Enter your app name and select "Embedded app"
  3. Configure App URLs

    • App URL: https://your-ngrok-url.ngrok.io
    • Allowed redirection URLs: https://your-ngrok-url.ngrok.io/api/auth/callback
  4. Set App Scopes

    • Go to "App setup" β†’ "Configuration"
    • Add required scopes (e.g., read_products, write_products)
  5. Get Your Credentials

    • Client ID (API Key): Found in "App setup" β†’ "App info"
    • Client Secret: Found in "App setup" β†’ "App info"

Step 2: Configure Environment Variables

Update your .env.local file:

# Shopify App Credentials
SHOPIFY_API_KEY=your_client_id_here
SHOPIFY_API_SECRET=your_client_secret_here
SCOPES=read_products,write_products
HOST=https://your-ngrok-url.ngrok.io

# Public variables (accessible in browser)
NEXT_PUBLIC_SHOPIFY_API_KEY=your_client_id_here
NEXT_PUBLIC_HOST=https://your-ngrok-url.ngrok.io

Step 3: Update Shopify App Configuration

Edit shopify.app.toml:

name = "your-app-name"
client_id = "your_client_id_here"
application_url = "https://your-ngrok-url.ngrok.io"
embedded = true

[access_scopes]
scopes = "read_products,write_products"

[auth]
redirect_urls = [
  "https://your-ngrok-url.ngrok.io/api/auth/callback"
]

[webhooks]
api_version = "2024-10"

[build]
automatically_update_urls_on_dev = true
dev_store_url = "your-dev-store.myshopify.com"

πŸ—„οΈ Supabase Database Setup

Step 1: Create a Supabase Project

  1. Go to Supabase Dashboard

    • Visit supabase.com
    • Sign in or create an account
    • Click "New project"
  2. Configure Your Project

    • Choose your organization
    • Enter project name and database password
    • Select a region close to your users
    • Click "Create new project"

Step 2: Get Supabase Credentials

  1. Go to Project Settings

    • Click the gear icon in the sidebar
    • Go to "API" section
  2. Copy Required Values

    • Project URL: Your project's API URL
    • Service Role Key: The service_role secret key (⚠️ Keep this secure!)

Step 3: Add Supabase Environment Variables

Update your .env.local file:

# Supabase Configuration
SUPABASE_URL=https://your-project-id.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_here

Step 4: Create Database Tables

In your Supabase dashboard, go to the SQL Editor and run this schema:

-- Session storage table
CREATE TABLE session (
  id text PRIMARY KEY,
  shop text NOT NULL,
  access_token text,
  scope text,
  expires timestamptz,
  is_online boolean DEFAULT false,
  state text,
  api_key text,
  created_at timestamptz DEFAULT now(),
  updated_at timestamptz DEFAULT now()
);

-- Online access info for online tokens
CREATE TABLE online_access_info (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  session_id text UNIQUE REFERENCES session(id) ON DELETE CASCADE,
  expires_in integer,
  associated_user_scope text,
  created_at timestamptz DEFAULT now(),
  updated_at timestamptz DEFAULT now()
);

-- Associated user information
CREATE TABLE associated_user (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  online_access_info_id uuid UNIQUE REFERENCES online_access_info(id) ON DELETE CASCADE,
  user_id bigint,
  first_name text,
  last_name text,
  email text,
  account_owner boolean DEFAULT false,
  locale text,
  collaborator boolean DEFAULT false,
  email_verified boolean DEFAULT false,
  created_at timestamptz DEFAULT now(),
  updated_at timestamptz DEFAULT now()
);

-- App installation tracking
CREATE TABLE app_installation (
  shop text PRIMARY KEY,
  webhooks_registered boolean DEFAULT false,
  installed_at timestamptz,
  uninstalled_at timestamptz
);

-- Performance indexes
CREATE INDEX idx_session_shop ON session(shop);
CREATE INDEX idx_session_api_key ON session(api_key);
CREATE INDEX idx_app_installation_shop ON app_installation(shop);

Step 5: Configure Row Level Security (Optional but Recommended)

For additional security, you can enable RLS:

-- Enable RLS on all tables
ALTER TABLE session ENABLE ROW LEVEL SECURITY;
ALTER TABLE online_access_info ENABLE ROW LEVEL SECURITY;
ALTER TABLE associated_user ENABLE ROW LEVEL SECURITY;
ALTER TABLE app_installation ENABLE ROW LEVEL SECURITY;

-- Since we're using service role key, we can create policies that allow all operations
-- In production, you might want more granular policies
CREATE POLICY "Service role can do everything" ON session FOR ALL USING (true);
CREATE POLICY "Service role can do everything" ON online_access_info FOR ALL USING (true);
CREATE POLICY "Service role can do everything" ON associated_user FOR ALL USING (true);
CREATE POLICY "Service role can do everything" ON app_installation FOR ALL USING (true);

πŸ”§ Development Setup

Step 1: Start ngrok

In a terminal, start ngrok to create a secure tunnel:

# Install ngrok if you haven't already
# Visit https://ngrok.com/ for installation instructions

# Start ngrok on port 3000
ngrok http 3000

Copy the https:// URL (e.g., https://abc123.ngrok.io) and update your environment variables and Shopify app configuration.

Step 2: Start the Development Server

npm run dev

Your app will be available at http://localhost:3000 and accessible via your ngrok URL.

Step 3: Install Your App

  1. Go to your Shopify Partner Dashboard
  2. Find your app and click "Test on development store"
  3. Select your development store
  4. Click "Install app"

πŸ§ͺ Testing Your Setup

Test API Authentication

# This should return 401/403 without proper auth
curl https://your-ngrok-url.ngrok.io/api/hello

# With proper Shopify session token, it should return 200
# (You'll get the session token from App Bridge in your embedded app)

Test Webhooks

  1. Install your app on a development store
  2. Check your Supabase database - you should see entries in app_installation
  3. Uninstall the app
  4. Check the database - the uninstalled_at field should be populated

Test GraphQL Codegen

# Generate types from GraphQL operations
npm run graphql-codegen

πŸ“š Key Concepts & Usage

Repository Pattern

All database operations use the repository pattern:

// Using the database service
import { dbService } from '@/lib/db/service';

// In an API route or server component
const session = await dbService.sessions.findById(sessionId);
const installation = await dbService.appInstallations.ensureWebhooksRegistered(shop);

Client-Side Data Fetching

Always use React Query hooks in components:

// ❌ Don't do this
const fetchData = async () => {
  const response = await fetch('/api/data');
  return response.json();
};

// βœ… Do this instead
import { useQuery } from '@tanstack/react-query';

function MyComponent() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['my-data'],
    queryFn: async () => {
      const response = await fetch('/api/data');
      return response.json();
    },
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error occurred</div>;
  
  return <div>{JSON.stringify(data)}</div>;
}

GraphQL Operations

  1. Create .graphql files in your app:
# app/graphql/products.graphql
query GetProducts($first: Int!) {
  products(first: $first) {
    edges {
      node {
        id
        title
        handle
      }
    }
  }
}
  1. Run codegen:
npm run graphql-codegen
  1. Use the generated types:
import { useGraphQL } from '@/app/hooks/useGraphQL';
import { GetProductsDocument } from '@/lib/gql/graphql';

function ProductsList() {
  const { data, isLoading } = useGraphQL(GetProductsDocument, { first: 10 });
  
  if (isLoading) return <div>Loading products...</div>;
  
  return (
    <div>
      {data?.products.edges.map(({ node }) => (
        <div key={node.id}>{node.title}</div>
      ))}
    </div>
  );
}

πŸš€ Production Deployment

Deploy to Vercel

  1. Connect Your Repository

  2. Configure Environment Variables

    • Add all variables from .env.local
    • Update HOST to your Vercel domain
  3. Update Shopify App URLs

    • Change app URL to your Vercel domain
    • Update redirect URLs accordingly

Deploy to Railway

  1. Connect Your Repository

  2. Configure Environment Variables

    • Add all environment variables
    • Railway will provide a domain automatically

πŸ”— Useful Links

Documentation

Tools & Services

GraphQL Tools

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ†˜ Support

If you encounter any issues:

  1. Check the SETUP.md for detailed setup instructions
  2. Review the troubleshooting section
  3. Open an issue on GitHub with detailed information about your problem

Built with ❀️ for the Shopify developer community

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors