Claude
Skills
Sign in
Back

typescript-sdk-specialist

Included with Lifetime
$97 forever

TypeScript SDK development with Node.js and browser support. Design SDK architecture, implement type-safe API clients, support ESM and CommonJS modules, and configure bundling for browsers.

Design

What this skill does


# typescript-sdk-specialist

You are **typescript-sdk-specialist** - a specialized skill for TypeScript SDK development, enabling creation of type-safe, tree-shakeable, and cross-platform API client libraries.

## Overview

This skill enables AI-powered TypeScript SDK development including:
- Designing TypeScript SDK architecture
- Implementing type-safe API clients
- Supporting ESM and CommonJS dual modules
- Configuring bundling for browsers
- Implementing retry logic and error handling
- Adding request/response interceptors
- Supporting multiple runtimes (Node.js, Deno, Bun, browsers)

## Prerequisites

- Node.js 18+ (or Bun/Deno)
- TypeScript 5.0+
- Package manager (npm, pnpm, or yarn)
- Build tools (tsup, esbuild, or Rollup)
- Testing framework (Vitest recommended)

## Capabilities

### 1. SDK Architecture Design

Design a modular, type-safe SDK architecture:

```typescript
// src/client.ts
import { BaseClient, ClientConfig } from './base';
import { UsersApi } from './api/users';
import { OrdersApi } from './api/orders';
import { AuthInterceptor } from './interceptors/auth';
import { RetryInterceptor } from './interceptors/retry';

export interface SDKConfig extends ClientConfig {
  apiKey?: string;
  accessToken?: string;
  timeout?: number;
  retries?: number;
  baseUrl?: string;
}

export class MyServiceSDK {
  private readonly client: BaseClient;

  // API namespaces
  public readonly users: UsersApi;
  public readonly orders: OrdersApi;

  constructor(config: SDKConfig) {
    this.client = new BaseClient({
      baseUrl: config.baseUrl ?? 'https://api.myservice.com',
      timeout: config.timeout ?? 30000,
      interceptors: [
        new AuthInterceptor(config),
        new RetryInterceptor({ maxRetries: config.retries ?? 3 })
      ]
    });

    // Initialize API namespaces
    this.users = new UsersApi(this.client);
    this.orders = new OrdersApi(this.client);
  }

  /**
   * Create SDK instance with API key authentication
   */
  static withApiKey(apiKey: string, config?: Partial<SDKConfig>): MyServiceSDK {
    return new MyServiceSDK({ ...config, apiKey });
  }

  /**
   * Create SDK instance with OAuth token
   */
  static withAccessToken(accessToken: string, config?: Partial<SDKConfig>): MyServiceSDK {
    return new MyServiceSDK({ ...config, accessToken });
  }
}
```

### 2. Type-Safe API Client

Implement strongly-typed API methods:

```typescript
// src/api/users.ts
import { BaseClient, RequestOptions } from '../base';
import {
  User,
  CreateUserRequest,
  UpdateUserRequest,
  ListUsersParams,
  PaginatedResponse
} from '../models';

export class UsersApi {
  constructor(private readonly client: BaseClient) {}

  /**
   * Get a user by ID
   * @param id - The user's unique identifier
   * @param options - Request options
   * @returns The user object
   * @throws {NotFoundError} When user doesn't exist
   * @throws {ApiError} On other API errors
   */
  async get(id: string, options?: RequestOptions): Promise<User> {
    return this.client.get<User>(`/users/${id}`, options);
  }

  /**
   * List users with pagination
   * @param params - Query parameters for filtering and pagination
   * @returns Paginated list of users
   */
  async list(params?: ListUsersParams): Promise<PaginatedResponse<User>> {
    return this.client.get<PaginatedResponse<User>>('/users', {
      params: {
        page: params?.page ?? 1,
        limit: params?.limit ?? 20,
        sort: params?.sort,
        filter: params?.filter
      }
    });
  }

  /**
   * Create a new user
   * @param data - User creation data
   * @returns The created user
   * @throws {ValidationError} When data is invalid
   */
  async create(data: CreateUserRequest): Promise<User> {
    return this.client.post<User>('/users', { body: data });
  }

  /**
   * Update an existing user
   * @param id - The user's unique identifier
   * @param data - Fields to update
   * @returns The updated user
   */
  async update(id: string, data: UpdateUserRequest): Promise<User> {
    return this.client.patch<User>(`/users/${id}`, { body: data });
  }

  /**
   * Delete a user
   * @param id - The user's unique identifier
   */
  async delete(id: string): Promise<void> {
    return this.client.delete(`/users/${id}`);
  }

  /**
   * Iterate over all users with automatic pagination
   * @param params - Query parameters
   * @yields User objects
   */
  async *listAll(params?: Omit<ListUsersParams, 'page'>): AsyncGenerator<User> {
    let page = 1;
    let hasMore = true;

    while (hasMore) {
      const response = await this.list({ ...params, page });

      for (const user of response.data) {
        yield user;
      }

      hasMore = response.hasMore;
      page++;
    }
  }
}
```

### 3. HTTP Client Base Implementation

Create a flexible HTTP client base:

```typescript
// src/base/client.ts
import { ApiError, NetworkError, TimeoutError } from '../errors';

export interface RequestOptions {
  params?: Record<string, string | number | boolean | undefined>;
  headers?: Record<string, string>;
  signal?: AbortSignal;
  timeout?: number;
}

export interface RequestInterceptor {
  onRequest?(config: RequestConfig): RequestConfig | Promise<RequestConfig>;
  onResponse?<T>(response: T): T | Promise<T>;
  onError?(error: Error): Error | Promise<Error>;
}

export class BaseClient {
  private baseUrl: string;
  private defaultTimeout: number;
  private interceptors: RequestInterceptor[];

  constructor(config: ClientConfig) {
    this.baseUrl = config.baseUrl;
    this.defaultTimeout = config.timeout ?? 30000;
    this.interceptors = config.interceptors ?? [];
  }

  async get<T>(path: string, options?: RequestOptions): Promise<T> {
    return this.request<T>('GET', path, options);
  }

  async post<T>(path: string, options?: RequestOptions & { body?: unknown }): Promise<T> {
    return this.request<T>('POST', path, options);
  }

  async put<T>(path: string, options?: RequestOptions & { body?: unknown }): Promise<T> {
    return this.request<T>('PUT', path, options);
  }

  async patch<T>(path: string, options?: RequestOptions & { body?: unknown }): Promise<T> {
    return this.request<T>('PATCH', path, options);
  }

  async delete<T = void>(path: string, options?: RequestOptions): Promise<T> {
    return this.request<T>('DELETE', path, options);
  }

  private async request<T>(
    method: string,
    path: string,
    options?: RequestOptions & { body?: unknown }
  ): Promise<T> {
    let config: RequestConfig = {
      method,
      url: `${this.baseUrl}${path}`,
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        ...options?.headers
      },
      params: options?.params,
      body: options?.body,
      timeout: options?.timeout ?? this.defaultTimeout,
      signal: options?.signal
    };

    // Apply request interceptors
    for (const interceptor of this.interceptors) {
      if (interceptor.onRequest) {
        config = await interceptor.onRequest(config);
      }
    }

    try {
      const response = await this.fetch(config);

      let result = await this.parseResponse<T>(response);

      // Apply response interceptors
      for (const interceptor of this.interceptors) {
        if (interceptor.onResponse) {
          result = await interceptor.onResponse(result);
        }
      }

      return result;
    } catch (error) {
      let finalError = error as Error;

      // Apply error interceptors
      for (const interceptor of this.interceptors) {
        if (interceptor.onError) {
          finalError = await interceptor.onError(finalError);
        }
      }

      throw finalError;
    }
  }

  private async fetch(config: RequestConfig): Promise<Response> {
    const url = new URL(config.url);

    if (config.params) {
      for (const [key, value] of Object.entries(config.params)) {
        if (value !== undefined) {
          url.searchParams.set(key, String(value));
        }
      }
    }

    const controller = 

Related in Design