| name | apollo-reference-architecture |
| description | Implement Apollo.io reference architecture.
Use when designing Apollo integrations, establishing patterns,
or building production-grade sales intelligence systems.
Trigger with phrases like "apollo architecture", "apollo system design",
"apollo integration patterns", "apollo best practices architecture".
|
| allowed-tools | Read, Write, Edit, Bash(gh:*), Bash(curl:*) |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Apollo Reference Architecture
Overview
Production-ready reference architecture for Apollo.io integrations covering system design, data flows, and integration patterns.
Architecture Diagram
+------------------+ +------------------+ +------------------+
| Frontend | | API Gateway | | Apollo API |
| (React/Vue) |---->| (Express) |---->| (External) |
+------------------+ +------------------+ +------------------+
| |
v |
+------------------+ |
| Apollo Service |<----------------+
| (Business Logic)|
+------------------+
| | |
+-------------+ | +-------------+
v v v
+------------+ +------------+ +------------+
| Cache | | Database | | Queue |
| (Redis) | | (Postgres) | | (Bull) |
+------------+ +------------+ +------------+
Project Structure
src/
├── lib/
│ └── apollo/
│ ├── client.ts # Apollo API client
│ ├── cache.ts # Caching layer
│ ├── rate-limiter.ts # Rate limiting
│ ├── errors.ts # Custom errors
│ └── types.ts # TypeScript types
├── services/
│ └── apollo/
│ ├── search.service.ts # People/org search
│ ├── enrich.service.ts # Enrichment logic
│ ├── sequence.service.ts # Email sequences
│ └── sync.service.ts # Data synchronization
├── jobs/
│ └── apollo/
│ ├── enrich.job.ts # Background enrichment
│ ├── sync.job.ts # Periodic sync
│ └── cleanup.job.ts # Cache cleanup
├── routes/
│ └── api/
│ └── apollo/
│ ├── search.ts # Search endpoints
│ ├── enrich.ts # Enrichment endpoints
│ └── webhooks.ts # Webhook handlers
├── models/
│ ├── contact.model.ts # Contact entity
│ ├── company.model.ts # Company entity
│ └── engagement.model.ts # Email engagement
└── config/
└── apollo.config.ts # Apollo configuration
Core Components
1. Apollo Service Layer
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ApolloClient } from '../../lib/apollo/client';
import { ApolloCache } from '../../lib/apollo/cache';
import { Contact } from '../../models/contact.model';
import { Company } from '../../models/company.model';
@Injectable()
export class ApolloService {
constructor(
private readonly client: ApolloClient,
private readonly cache: ApolloCache,
@InjectRepository(Contact)
private readonly contactRepo: Repository<Contact>,
@InjectRepository(Company)
private readonly : <>,
) {}
(: ): <[]> {
searchResults = ..(criteria);
qualified = .(searchResults., criteria);
enriched = .(
qualified.(, ).( .(lead))
);
.(enriched);
enriched;
}
(: ): <> {
cached = ..();
(cached) cached;
[personData, companyData] = .([
..({ : lead. }),
lead.?.
? ..(lead..)
: ,
]);
enriched = .(lead, personData, companyData);
..(, enriched, );
enriched;
}
(: []): <> {
( lead leads) {
..({
: lead.,
: lead.,
: lead.,
: lead.,
: lead.,
: lead.?.,
: (),
}, []);
(lead.) {
..({
: lead..,
: lead..,
: lead..,
: lead..,
: lead..,
: (),
}, []);
}
}
}
}
2. Background Job Processing
import { Job, Queue } from 'bull';
import { Process, Processor } from '@nestjs/bull';
import { ApolloService } from '../../services/apollo/apollo.service';
interface EnrichJobData {
contactIds: string[];
priority: 'high' | 'normal' | 'low';
}
@Processor('apollo-enrich')
export class EnrichProcessor {
constructor(private readonly apolloService: ApolloService) {}
@Process('enrich-contacts')
async handleEnrich(job: Job<EnrichJobData>): Promise<void> {
const { contactIds, priority } = job.data;
const batchSize = priority === 'high' ? 10 : 5;
for (let i = ; i < contactIds.; i += batchSize) {
batch = contactIds.(i, i + batchSize);
.(
batch.( ..(id))
);
job.(((i + batchSize) / contactIds.) * );
(i + batchSize < contactIds.) {
( (r, ));
}
}
}
}
()
{
() {}
() {
..(, {
contactIds,
priority,
}, {
: priority === ? : priority === ? : ,
: ,
: {
: ,
: ,
},
});
}
}
3. Data Models
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, Index } from 'typeorm';
import { Company } from './company.model';
@Entity('contacts')
export class Contact {
@PrimaryGeneratedColumn('uuid')
id: string;
@Index({ unique: true })
@Column()
apolloId: string;
@Index()
@Column({ nullable: true })
email: string;
@Column()
name: string;
@Column({ nullable: true })
firstName: string;
@Column({ nullable: true })
lastName: string;
@Column({ nullable: true })
title: string;
@Column({ nullable: true })
: ;
({ : })
: ;
({ : })
: ;
({ : , : })
: <, >;
( , company.)
: ;
()
: ;
({ : })
: ;
({ : , : })
: ;
({ : , : })
: ;
}
()
{
()
: ;
({ : })
()
: ;
()
: ;
()
()
: ;
({ : })
: ;
({ : })
: ;
({ : })
: ;
({ : })
: ;
({ : })
: ;
({ : , : })
: ;
({ : , : })
: [];
({ : , : })
: {
: ;
: ;
: ;
};
( , contact.)
: [];
}
4. API Routes
import { Router } from 'express';
import { ApolloService } from '../../../services/apollo/apollo.service';
import { validateRequest } from '../../../middleware/validation';
const router = Router();
router.post('/search', validateRequest(SearchSchema), async (req, res) => {
const { domains, titles, locations, minEmployees, maxEmployees } = req.body;
const results = await apolloService.searchAndEnrich({
domains,
titles,
locations,
minEmployees,
maxEmployees,
});
res.json({
success: true,
data: results,
meta: {
count: results.length,
timestamp: new Date().toISOString(),
},
});
});
router.post('/enrich/bulk', validateRequest(BulkEnrichSchema), async (req, res) => {
const { contactIds, priority } = req.body;
await enrichQueue.enqueueContacts(contactIds, priority);
res.({
: ,
: ,
: ,
});
});
router;
Integration Patterns
CRM Integration (Salesforce)
export class SalesforceIntegration {
async syncContact(contact: Contact): Promise<void> {
const sfContact = await this.salesforce.sobject('Contact').upsert({
Email: contact.email,
FirstName: contact.firstName,
LastName: contact.lastName,
Title: contact.title,
Apollo_ID__c: contact.apolloId,
LinkedIn_URL__c: contact.linkedinUrl,
}, 'Email');
console.log(`Synced contact ${contact.email} to Salesforce`);
}
async syncCompany(company: Company): Promise<void> {
const sfAccount = await this.salesforce.sobject('Account').upsert({
Name: company.,
: ,
: company.,
: company.,
: company.,
}, );
}
}
Event-Driven Architecture
export const APOLLO_EVENTS = {
CONTACT_ENRICHED: 'apollo.contact.enriched',
COMPANY_ENRICHED: 'apollo.company.enriched',
SEARCH_COMPLETED: 'apollo.search.completed',
SEQUENCE_STARTED: 'apollo.sequence.started',
EMAIL_ENGAGEMENT: 'apollo.email.engagement',
};
eventBus.on(APOLLO_EVENTS.CONTACT_ENRICHED, async (contact) => {
await salesforceIntegration.syncContact(contact);
await searchIndex.indexContact(contact);
if (contact.score >= 80) {
await slackNotifier.sendHighValueLead(contact);
}
});
Output
- Layered architecture (client, service, job, model)
- Background job processing with Bull
- Database models with TypeORM
- RESTful API endpoints
- CRM integration patterns
- Event-driven architecture
Error Handling
| Layer | Strategy |
|---|
| Client | Retry with backoff |
| Service | Graceful degradation |
| Jobs | Dead letter queue |
| API | Structured error responses |
Resources
Next Steps
Proceed to apollo-multi-env-setup for environment configuration.