From hubspot-pack
Guides HubSpot CRM integration architectures: embedded client for MVPs (<50K API calls/day), service layer with queues/cache for growth (50K-300K), and gateway patterns for scale.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin hubspot-packThis skill is limited to using the following tools:
Three validated architecture patterns for HubSpot CRM integrations at different scales, from embedded client to dedicated API gateway.
Provides layered TypeScript architecture for HubSpot CRM integrations with typed clients, services, caching, webhooks, API routes, and project structure.
Provides expert patterns for HubSpot CRM integration including OAuth authentication, CRM objects, associations, batch operations, webhooks, and custom objects using Node.js and Python SDKs.
Provides expert patterns for HubSpot CRM integration including OAuth authentication, CRM objects, associations, batch operations, webhooks, and custom objects using Node.js/Python SDKs.
Share bugs, ideas, or general feedback.
Three validated architecture patterns for HubSpot CRM integrations at different scales, from embedded client to dedicated API gateway.
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
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
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
| Factor | Embedded | Service Layer | Gateway |
|---|---|---|---|
| Team size | 1-3 | 3-15 | 15+ |
| Contacts | < 10K | 10K-100K | 100K+ |
| Services using CRM | 1 | 1-2 | 3+ |
| Sync model | Synchronous | Async queue | Event-driven |
| Cache | In-memory | Redis | Redis + CDN |
| Rate limit mgmt | SDK built-in | App-level | Centralized |
| Fault isolation | None | Partial | Full |
| Time to build | Days | 1-2 weeks | 3-4 weeks |
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
| Issue | Cause | Solution |
|---|---|---|
| Over-engineering | Wrong variant choice | Start with Embedded, evolve |
| Rate limit shared | Multiple services hitting HubSpot | Move to Gateway pattern |
| Cache inconsistency | No invalidation strategy | Invalidate on webhook events |
| Gateway single point of failure | No redundancy | Run multiple replicas + HPA |
For common anti-patterns, see hubspot-known-pitfalls.