| name | clickup-sdk-patterns |
| description | Production-ready ClickUp API v2 client patterns with typed wrappers,
error handling, caching, and multi-tenant support.
Trigger: "clickup client wrapper", "clickup SDK patterns", "clickup best practices",
"clickup typescript client", "clickup API wrapper", "production clickup code".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
| tags | ["saas","productivity","clickup"] |
| compatible-with | claude-code |
ClickUp SDK Patterns
Overview
ClickUp has no official SDK. Build a typed REST client wrapper around https://api.clickup.com/api/v2/. These patterns provide singleton clients, typed responses, error boundaries, and multi-tenant support.
Typed Client Wrapper
const CLICKUP_BASE = 'https://api.clickup.com/api/v2';
interface ClickUpClientConfig {
token: string;
timeout?: number;
onRateLimit?: (waitMs: number) => void;
}
class ClickUpClient {
private token: string;
private timeout: number;
private rateLimitRemaining = 100;
private rateLimitReset = 0;
constructor(config: ClickUpClientConfig) {
this.token = config.token;
this.timeout = config.timeout ?? 30000;
}
async request<T>(path: string, options: RequestInit = {}): Promise<T> {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), .);
{
response = (, {
...options,
: controller.,
: {
: .,
: ,
...options.,
},
});
. = (
response..() ??
);
. = (
response..() ??
) * ;
(!response.) {
body = response.().( ({}));
(response., body., body.);
}
response.();
} {
(timer);
}
}
(): <> {
data = .<{ : }>();
data.;
}
(): <[]> {
data = .<{ : [] }>();
data.;
}
(: ): <[]> {
data = .<{ : [] }>(
);
data.;
}
(: , : ): <> {
.<>(, {
: ,
: .(task),
});
}
(: ): <> {
.<>();
}
(: , : <>): <> {
.<>(, {
: ,
: .(updates),
});
}
(): {
. < && .() < .;
}
}
TypeScript Types
interface ClickUpUser {
id: number;
username: string;
email: string;
color: string;
profilePicture: string | null;
}
interface ClickUpTeam {
id: string;
name: string;
color: string;
members: Array<{ user: ClickUpUser; role: number }>;
}
interface ClickUpSpace {
id: string;
name: string;
private: boolean;
statuses: Array<{ status: string; color: string; type: string }>;
features: Record<string, { enabled: boolean }>;
}
interface ClickUpTask {
id: string;
custom_id: string | ;
: ;
: ;
: { : ; : ; : };
: { : ; : ; : } | ;
: ;
: ;
: | ;
: [];
: <{ : }>;
: ;
: { : ; : };
: { : ; : };
: { : };
: [];
}
{
: ;
?: ;
?: ;
?: [];
?: | | | | ;
?: ;
?: ;
?: ;
?: ;
?: [];
?: <{ : ; : }>;
}
{
: ;
: ;
: ;
: ;
}
{
() {
();
}
(): { . === ; }
(): { . === ; }
(): { . === ; }
(): { . === || . >= ; }
}
Singleton Pattern
let defaultClient: ClickUpClient | null = null;
export function getClickUpClient(): ClickUpClient {
if (!defaultClient) {
const token = process.env.CLICKUP_API_TOKEN;
if (!token) throw new Error('CLICKUP_API_TOKEN not set');
defaultClient = new ClickUpClient({ token });
}
return defaultClient;
}
Multi-Tenant Factory
const tenantClients = new Map<string, ClickUpClient>();
function getClientForTenant(tenantId: string, token: string): ClickUpClient {
if (!tenantClients.has(tenantId)) {
tenantClients.set(tenantId, new ClickUpClient({ token }));
}
return tenantClients.get(tenantId)!;
}
Zod Response Validation
import { z } from 'zod';
const TaskSchema = z.object({
id: z.string(),
name: z.string(),
status: z.object({ status: z.string(), color: z.string() }),
priority: z.object({ priority: z.string() }).nullable(),
url: z.string().url(),
});
async function getValidatedTask(taskId: string) {
const raw = await getClickUpClient().getTask(taskId);
return TaskSchema.parse(raw);
}
Error Handling
| Pattern | Use Case | Benefit |
|---|
| Typed error class | All API calls | Type-safe error discrimination |
| Singleton | Single-tenant apps | Shared rate limit tracking |
| Factory | Multi-tenant SaaS | Per-tenant isolation |
| Zod validation | Response parsing | Catches API contract changes |
Resources
Next Steps
Apply patterns in clickup-core-workflow-a for task management.