Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Choose and implement Canva Connect API architecture blueprints for different scales. Use when designing new Canva integrations, choosing between monolith/service/microservice architectures, or planning migration paths. Trigger with phrases like "canva architecture", "canva blueprint", "how to structure canva", "canva project layout", "canva microservice".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 35% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 52% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 17% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 50% | 0% |
Three validated architecture patterns for Canva Connect API integrations. All use the REST API at api.canva.com/rest/v1/* with OAuth 2.0 PKCE tokens. The key architectural decision is how to handle token storage, async operations (exports, autofills), and rate limit management.
Best for: MVPs, small teams, < 100 Canva users
my-app/
├── src/
│ ├── canva/
│ │ ├── client.ts # REST client with auto-refresh
│ │ ├── auth.ts # OAuth PKCE flow
│ │ └── types.ts
│ ├── routes/
│ │ ├── auth.ts # OAuth callback
│ │ └── designs.ts # Design CRUD
│ ├── store/
│ │ └── tokens.ts # SQLite/file token store
│ └── index.tstypescript// Direct API calls in route handlers app.post('/api/designs', async (req, res) => { const canva = getClientForUser(req.user.id); const { design } = await canva.request('/designs', { method: 'POST', body: JSON.stringify({ design_type: { type: 'custom', width: 1080, height: 1080 }, title: req.body.title, }), }); res.json({ designId: design.id, editUrl: design.urls.edit_url }); });
Pros: Fast to build, simple token management, easy to debug. Cons: Synchronous exports block requests, no job queue for autofills.
Best for: Growing apps, 100-1,000 users, multiple Canva features
my-app/
├── src/
│ ├── canva/
│ │ ├── client.ts
│ │ └── auth.ts
│ ├── services/
│ │ ├── design.service.ts # Business logic + caching
│ │ ├── export.service.ts # Async export with polling
│ │ ├── asset.service.ts # Upload management
│ │ └── template.service.ts # Autofill orchestration
│ ├── queue/
│ │ └── export-worker.ts # Background export processing
│ ├── routes/
│ └── store/
│ └── tokens.ts # PostgreSQL encrypted tokenstypescript// Service layer handles caching, retry, and async operations class ExportService { constructor( private canva: CanvaClient, private cache: Redis, private queue: Bull.Queue ) {} async exportDesign(designId: string, format: object): Promise<string> { // Check cache for recent export const cached = await this.cache.get(`export:${designId}:${JSON.stringify(format)}`); if (cached) return cached; // Queue export job — don't block the request const job = await this.queue.add('canva-export', { designId, format }); return job.id; } } // Background worker polls Canva export API exportQueue.process('canva-export', async (job) => { const { designId, format } = job.data; const canva = await getServiceClient(); const { job: exportJob } = await canva.request('/exports', { method: 'POST', body: JSON.stringify({ design_id: designId, format }), }); // Poll for completion let result = exportJob; while (result.status === 'in_progress') { await new Promise(r => setTimeout(r, 2000)); const poll = await canva.request(`/exports/${result.id}`); result = poll.job; } return result.status === 'success' ? result.urls : null; });
Pros: Non-blocking exports, caching, separation of concerns. Cons: More infrastructure (Redis, job queue), more complex deployment.
Best for: 1,000+ users, multi-team, strict SLAs, Canva Enterprise with autofill
canva-service/ # Dedicated microservice
├── src/
│ ├── api/
│ │ └── grpc/ # Internal gRPC API
│ ├── canva/
│ │ ├── client.ts
│ │ └── auth.ts
│ ├── services/
│ ├── workers/
│ │ ├── export.worker.ts
│ │ ├── autofill.worker.ts
│ │ └── webhook.worker.ts
│ └── store/
│ └── tokens.ts # Vault-backed token storage
├── k8s/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── hpa.yaml # Scale based on queue depthKey differences:
| Factor | Monolith | Service Layer | Microservice | |--------|----------|---------------|--------------| | Users | < 100 | 100-1,000 | 1,000+ | | Team Size | 1-3 | 3-10 | 10+ | | Export Volume | < 100/day | 100-2,000/day | 2,000-5,000/day | | Canva Tier | Free/Pro | Pro/Teams | Enterprise | | Infrastructure | Single server | App + Redis + queue | Kubernetes | | Time to Build | 1-2 days | 1-2 weeks | 2-4 weeks |
Monolith → Service Layer:
1. Extract canva/ to services/
2. Add Redis for caching
3. Add BullMQ for async exports
4. Move token store to PostgreSQL
Service Layer → Microservice:
1. Create canva-service repository
2. Define gRPC contract
3. Add per-operation workers
4. Deploy to Kubernetes
5. Migrate token store to Vault| Issue | Cause | Solution | |-------|-------|----------| | Over-engineering | Wrong variant | Start simpler, migrate when needed | | Export blocking requests | No job queue (Variant A) | Queue with BullMQ | | Token management complex | Multi-user | Use factory pattern per user | | Integration export quota | > 5,000/day | Contact Canva for increase |
For common anti-patterns, see canva-known-pitfalls.
Other measured skills in the registry, with their headline benchmark lift.