Claude-code-plugins-plus-skills hubspot-architecture-variants

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/hubspot-pack/skills/hubspot-architecture-variants" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-hubspot-architecture-variants && rm -rf "$T"
manifest: plugins/saas-packs/hubspot-pack/skills/hubspot-architecture-variants/SKILL.md
source content

HubSpot Architecture Variants

Overview

Three validated architecture patterns for HubSpot CRM integrations at different scales, from embedded client to dedicated API gateway.

Prerequisites

  • Understanding of team size and daily API call volume
  • Knowledge of deployment infrastructure
  • Clear sync requirements (real-time vs batch)

Instructions

Variant A: Embedded Client (Simple)

Best for: MVPs, small teams, < 10K contacts, < 50K API calls/day

your-app/
├── src/
│   ├── hubspot/
│   │   ├── client.ts       # @hubspot/api-client singleton
│   │   └── contacts.ts     # Direct CRM operations
│   ├── routes/
│   │   └── api.ts           # API routes that call HubSpot directly
│   └── index.ts
// Direct integration in route handlers
import * as hubspot from '@hubspot/api-client';

const client = new hubspot.Client({
  accessToken: process.env.HUBSPOT_ACCESS_TOKEN!,
  numberOfApiCallRetries: 3,
});

app.get('/api/contacts', async (req, res) => {
  const page = await client.crm.contacts.basicApi.getPage(
    20, req.query.after as string, ['email', 'firstname', 'lastname']
  );
  res.json(page);
});

app.post('/api/contacts', async (req, res) => {
  const contact = await client.crm.contacts.basicApi.create({
    properties: req.body,
    associations: [],
  });
  res.status(201).json(contact);
});

Pros: Fast to build, simple to understand, one deployment Cons: No fault isolation, HubSpot latency in user request path


Variant B: Service Layer with Async Queue

Best for: Growing teams, 10K-100K contacts, 50K-300K API calls/day

your-app/
├── src/
│   ├── services/
│   │   └── hubspot/
│   │       ├── client.ts       # Singleton with circuit breaker
│   │       ├── contact.ts      # Business logic layer
│   │       ├── deal.ts
│   │       └── sync.ts         # Background sync
│   ├── queue/
│   │   └── hubspot-worker.ts   # Process async operations
│   ├── cache/
│   │   └── hubspot-cache.ts    # Redis cache layer
│   ├── routes/
│   └── index.ts
// Service layer abstracts HubSpot from route handlers
class ContactService {
  private client = getHubSpotClient();
  private cache: Redis;
  private queue: BullQueue;

  async getContact(id: string): Promise<Contact> {
    // Check cache first
    const cached = await this.cache.get(`contact:${id}`);
    if (cached) return JSON.parse(cached);

    // Fetch from HubSpot
    const contact = await this.client.crm.contacts.basicApi.getById(
      id, ['email', 'firstname', 'lastname', 'lifecyclestage']
    );

    // Cache for 5 minutes
    await this.cache.setex(`contact:${id}`, 300, JSON.stringify(contact));
    return contact;
  }

  async createContact(data: CreateContactInput): Promise<{ jobId: string }> {
    // Enqueue for async processing (fast API response)
    const job = await this.queue.add('create-contact', data);
    return { jobId: job.id };
  }
}

// Background worker
queue.process('create-contact', async (job) => {
  const contact = await client.crm.contacts.basicApi.create({
    properties: job.data,
    associations: [],
  });
  // Invalidate cache, send notification, etc.
  await cache.del(`contacts:list`);
  return contact;
});

Pros: Fault isolation, fast API responses, caching, background processing Cons: More complexity, Redis dependency, two process types


Variant C: Dedicated HubSpot Gateway

Best for: Enterprise, 100K+ contacts, multiple services needing CRM access

hubspot-gateway/               # Standalone service
├── src/
│   ├── api/
│   │   ├── grpc/              # Internal gRPC API
│   │   │   └── hubspot.proto
│   │   └── rest/              # REST API (optional)
│   ├── domain/
│   │   ├── contacts.ts        # Domain logic
│   │   ├── deals.ts
│   │   └── sync.ts
│   ├── infrastructure/
│   │   ├── hubspot-client.ts  # SDK wrapper
│   │   ├── rate-limiter.ts    # Centralized rate limiting
│   │   ├── cache.ts           # Redis cache
│   │   └── circuit-breaker.ts
│   └── index.ts
├── k8s/
│   ├── deployment.yaml
│   └── hpa.yaml

other-services/
├── order-service/      # Calls hubspot-gateway
├── marketing-service/  # Calls hubspot-gateway
└── analytics-service/  # Calls hubspot-gateway
// All HubSpot access goes through the gateway
// Gateway handles rate limiting, caching, and circuit breaking

// Centralized rate limiter ensures all services share the 10 req/sec limit
class CentralizedRateLimiter {
  private redis: Redis;
  private maxPerSecond = 8; // leave headroom

  async acquire(): Promise<void> {
    const key = `hubspot:ratelimit:${Math.floor(Date.now() / 1000)}`;
    const count = await this.redis.incr(key);
    await this.redis.expire(key, 2);

    if (count > this.maxPerSecond) {
      throw new Error('HubSpot rate limit -- waiting');
    }
  }
}

Pros: Single point of rate limiting, all services share cache, independent scaling Cons: Network hop, operational complexity, gRPC/REST contract management


Decision Matrix

FactorEmbeddedService LayerGateway
Team size1-33-1515+
Contacts< 10K10K-100K100K+
Services using CRM11-23+
Sync modelSynchronousAsync queueEvent-driven
CacheIn-memoryRedisRedis + CDN
Rate limit mgmtSDK built-inApp-levelCentralized
Fault isolationNonePartialFull
Time to buildDays1-2 weeks3-4 weeks

Migration Path

Embedded → Service Layer:
  1. Extract HubSpot client to services/hubspot/
  2. Add Redis cache layer
  3. Move writes to background queue

Service Layer → Gateway:
  1. Extract to standalone service
  2. Define gRPC/REST contract
  3. Add centralized rate limiter
  4. Migrate services one at a time

Output

  • Three validated architecture patterns with code examples
  • Decision matrix for choosing the right pattern
  • Migration path from simpler to more complex architectures

Error Handling

IssueCauseSolution
Over-engineeringWrong variant choiceStart with Embedded, evolve
Rate limit sharedMultiple services hitting HubSpotMove to Gateway pattern
Cache inconsistencyNo invalidation strategyInvalidate on webhook events
Gateway single point of failureNo redundancyRun multiple replicas + HPA

Resources

Next Steps

For common anti-patterns, see

hubspot-known-pitfalls
.