Agent Skills: Klaviyo Local Dev Loop

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/klaviyo-local-dev-loop

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/klaviyo-pack/skills/klaviyo-local-dev-loop

Skill Files

Browse the full folder contents for klaviyo-local-dev-loop.

Download Skill

Loading file tree…

plugins/saas-packs/klaviyo-pack/skills/klaviyo-local-dev-loop/SKILL.md

Skill Metadata

Name
klaviyo-local-dev-loop
Description
'Configure Klaviyo local development with hot reload, mocking, and testing.

Klaviyo Local Dev Loop

Overview

Set up a fast, reproducible local development workflow for Klaviyo integrations with hot reload, SDK mocking, and integration tests.

Prerequisites

  • Completed klaviyo-install-auth setup
  • Node.js 18+ with npm/pnpm
  • klaviyo-api package installed

Instructions

Step 1: Project Structure

my-klaviyo-project/
├── src/
│   ├── klaviyo/
│   │   ├── client.ts          # ApiKeySession + API class singletons
│   │   ├── profiles.ts        # Profile operations
│   │   ├── events.ts          # Event tracking
│   │   └── lists.ts           # List management
│   └── index.ts
├── tests/
│   ├── unit/
│   │   └── profiles.test.ts   # Mocked SDK tests
│   └── integration/
│       └── klaviyo.test.ts    # Live API tests (CI only)
├── .env.local                 # Local secrets (git-ignored)
├── .env.example               # Template for team
├── .env.test                  # Test environment (sandbox key)
└── package.json

Step 2: Environment Configuration

# .env.example (commit this)
KLAVIYO_PRIVATE_KEY=pk_your_test_key_here
KLAVIYO_PUBLIC_KEY=YourPublicKey
NODE_ENV=development

# .env.local (git-ignored, actual secrets)
KLAVIYO_PRIVATE_KEY=pk_********************************
// package.json scripts
{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "test": "vitest",
    "test:watch": "vitest --watch",
    "test:integration": "KLAVIYO_TEST=1 vitest --config vitest.integration.config.ts",
    "typecheck": "tsc --noEmit"
  }
}

Step 3: SDK Client Singleton

// src/klaviyo/client.ts
import {
  ApiKeySession,
  ProfilesApi,
  EventsApi,
  ListsApi,
  SegmentsApi,
  CampaignsApi,
  MetricsApi,
} from 'klaviyo-api';

let session: ApiKeySession | null = null;

function getSession(): ApiKeySession {
  if (!session) {
    const key = process.env.KLAVIYO_PRIVATE_KEY;
    if (!key) throw new Error('KLAVIYO_PRIVATE_KEY not set');
    session = new ApiKeySession(key);
  }
  return session;
}

// Lazy singletons -- only instantiate what you use
export const profiles = () => new ProfilesApi(getSession());
export const events = () => new EventsApi(getSession());
export const lists = () => new ListsApi(getSession());
export const segments = () => new SegmentsApi(getSession());
export const campaigns = () => new CampaignsApi(getSession());
export const metrics = () => new MetricsApi(getSession());

Step 4: Unit Testing with Mocks

// tests/unit/profiles.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock the entire klaviyo-api module
vi.mock('klaviyo-api', () => ({
  ApiKeySession: vi.fn(),
  ProfilesApi: vi.fn().mockImplementation(() => ({
    createProfile: vi.fn().mockResolvedValue({
      body: {
        data: {
          id: '01JMOCKPROFILEID',
          type: 'profile',
          attributes: { email: 'test@example.com', firstName: 'Test' },
        },
      },
    }),
    getProfiles: vi.fn().mockResolvedValue({
      body: {
        data: [{ id: '01JMOCKPROFILEID', attributes: { email: 'test@example.com' } }],
        links: { next: null },
      },
    }),
  })),
  ProfileEnum: { Profile: 'profile' },
}));

import { ProfilesApi, ApiKeySession } from 'klaviyo-api';

describe('Profile operations', () => {
  let profilesApi: ProfilesApi;

  beforeEach(() => {
    const session = new ApiKeySession('pk_test_key');
    profilesApi = new ProfilesApi(session);
  });

  it('creates a profile with email', async () => {
    const result = await profilesApi.createProfile({
      data: {
        type: 'profile' as any,
        attributes: { email: 'test@example.com', firstName: 'Test' },
      },
    });
    expect(result.body.data.id).toBe('01JMOCKPROFILEID');
  });
});

Step 5: Integration Test (runs against live API)

// tests/integration/klaviyo.test.ts
import { describe, it, expect } from 'vitest';
import { ApiKeySession, ProfilesApi, AccountsApi } from 'klaviyo-api';

const SKIP = !process.env.KLAVIYO_TEST;

describe.skipIf(SKIP)('Klaviyo Integration', () => {
  const session = new ApiKeySession(process.env.KLAVIYO_PRIVATE_KEY!);

  it('connects to Klaviyo account', async () => {
    const accountsApi = new AccountsApi(session);
    const result = await accountsApi.getAccounts();
    expect(result.body.data).toHaveLength(1);
    expect(result.body.data[0].id).toBeTruthy();
  });

  it('creates and retrieves a test profile', async () => {
    const profilesApi = new ProfilesApi(session);
    const testEmail = `test-${Date.now()}@example.com`;

    await profilesApi.createProfile({
      data: {
        type: 'profile' as any,
        attributes: { email: testEmail, firstName: 'IntegrationTest' },
      },
    });

    const profiles = await profilesApi.getProfiles({
      filter: `equals(email,"${testEmail}")`,
    });
    expect(profiles.body.data[0].attributes.firstName).toBe('IntegrationTest');
  });
});

Step 6: Hot Reload Development

# Start dev server with file watching
npm run dev

# In another terminal, run tests on change
npm run test:watch

Output

  • Working dev environment with hot reload via tsx watch
  • Unit tests with mocked klaviyo-api SDK
  • Integration tests gated behind KLAVIYO_TEST=1
  • Client singleton pattern for consistent SDK usage

Error Handling

| Error | Cause | Solution | |-------|-------|----------| | KLAVIYO_PRIVATE_KEY not set | Missing .env.local | Copy from .env.example | | Mock type errors | SDK type mismatches | Use as any for mock enum values | | Integration test 429 | Rate limited in CI | Add delays between tests or use test key | | tsx not found | Missing dependency | npm install -D tsx |

Resources

Next Steps

See klaviyo-sdk-patterns for production-ready code patterns.