Claude-code-plugins-plus-skills clerk-local-dev-loop

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/clerk-pack/skills/clerk-local-dev-loop" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-clerk-local-dev-loop && rm -rf "$T"
manifest: plugins/saas-packs/clerk-pack/skills/clerk-local-dev-loop/SKILL.md
source content

Clerk Local Dev Loop

Overview

Configure an efficient local development workflow with Clerk authentication, including test users, hot reload, and mock auth for unit tests.

Prerequisites

  • Clerk SDK installed (
    clerk-install-auth
    completed)
  • Development instance created in Clerk Dashboard
  • Node.js development environment

Instructions

Step 1: Configure Development Instance

Create a separate Clerk development instance to isolate test data from production.

# .env.local — use test keys (pk_test_ / sk_test_)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...

# Optional: Enable Clerk debug logging
CLERK_DEBUG=true

Clerk development instances provide:

  • No email verification required
  • Test phone numbers accepted
  • Relaxed rate limits
  • OAuth with test credentials

Step 2: Set Up Test Users

// scripts/seed-test-users.ts
import { createClerkClient } from '@clerk/backend'

const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })

async function seedTestUsers() {
  const users = [
    { emailAddress: ['admin@test.com'], password: 'test1234', firstName: 'Admin', lastName: 'User' },
    { emailAddress: ['member@test.com'], password: 'test1234', firstName: 'Member', lastName: 'User' },
  ]

  for (const user of users) {
    try {
      await clerk.users.createUser(user)
      console.log(`Created: ${user.emailAddress[0]}`)
    } catch (err: any) {
      if (err.status === 422) {
        console.log(`Already exists: ${user.emailAddress[0]}`)
      } else {
        throw err
      }
    }
  }
}

seedTestUsers()

Run with:

npx tsx scripts/seed-test-users.ts

Step 3: Configure Hot Reload

// next.config.ts — ensure Clerk works with hot reload
const nextConfig = {
  // Clerk SDK is compatible with Turbopack
  experimental: {
    // turbo: {}, // Uncomment if using Turbopack
  },
  // Prevent Clerk SDK from being bundled incorrectly
  serverExternalPackages: ['@clerk/backend'],
}

export default nextConfig
# Start dev server with HTTPS (required for some Clerk features)
npx next dev --experimental-https

Step 4: Development Scripts

// package.json scripts
{
  "scripts": {
    "dev": "next dev",
    "dev:https": "next dev --experimental-https",
    "dev:seed": "tsx scripts/seed-test-users.ts",
    "dev:tunnel": "ngrok http 3000",
    "dev:webhook": "ngrok http 3000 --log stdout"
  }
}

Step 5: Mock Authentication for Tests

// test/helpers/clerk-mock.ts
import { vi } from 'vitest'

export function mockClerkAuth(overrides: { userId?: string; orgId?: string } = {}) {
  const mockAuth = {
    userId: overrides.userId || 'user_test_123',
    orgId: overrides.orgId || null,
    getToken: vi.fn().mockResolvedValue('mock_token'),
    has: vi.fn().mockReturnValue(true),
  }

  vi.mock('@clerk/nextjs/server', () => ({
    auth: vi.fn().mockResolvedValue(mockAuth),
    currentUser: vi.fn().mockResolvedValue({
      id: mockAuth.userId,
      firstName: 'Test',
      lastName: 'User',
      emailAddresses: [{ emailAddress: 'test@test.com' }],
    }),
    clerkMiddleware: vi.fn(() => (req: any, res: any, next: any) => next()),
  }))

  return mockAuth
}
// test/api/data.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { mockClerkAuth } from '../helpers/clerk-mock'

describe('GET /api/data', () => {
  beforeEach(() => {
    mockClerkAuth({ userId: 'user_test_123' })
  })

  it('returns data for authenticated user', async () => {
    const res = await fetch('/api/data')
    expect(res.status).toBe(200)
  })
})

Output

  • Development instance with test keys configured
  • Test users seeded via script
  • HTTPS dev server running for Clerk compatibility
  • Vitest mock helpers for auth in unit tests

Error Handling

ErrorCauseSolution
Dev/prod key mismatchUsing
pk_live_
in dev
Switch to
pk_test_
/
sk_test_
keys
SSL requiredClerk needs HTTPS locallyRun
next dev --experimental-https
Cookies not setWrong domain configCheck Clerk Dashboard domain settings
Session not persistingBrowser storage issueClear cookies, check localhost domain

Examples

Playwright Auth Fixture for E2E Tests

// e2e/fixtures/auth.ts
import { test as base } from '@playwright/test'

export const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    await page.goto('/sign-in')
    await page.fill('input[name="identifier"]', 'admin@test.com')
    await page.click('button:has-text("Continue")')
    await page.fill('input[name="password"]', 'test1234')
    await page.click('button:has-text("Continue")')
    await page.waitForURL('/dashboard')
    await use(page)
  },
})

Resources

Next Steps

Proceed to

clerk-sdk-patterns
for common SDK usage patterns.