Реалізація SDK/бібліотеки для інтеграцій із SaaS-застосунком

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація SDK/бібліотеки для інтеграцій із SaaS-застосунком
Складна
~2-4 тижні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація SDK/библіотеки для інтеграцій з SaaS-додатком

SDK (Software Development Kit) — готова бібліотека для клієнтів API. Знижує барієр інтеграції: замість роботи з HTTP напрямку розробник використовує типізовані методи.

Структура SDK-проекту

my-api-sdk/
  src/
    client.ts          # Основний HTTP-клієнт
    resources/
      users.ts         # Ресурс: користувачі
      projects.ts      # Ресурс: проекти
      webhooks.ts      # Верифікація webhook
    types/
      index.ts         # Усі публічні типи
      responses.ts     # Типи відповідей API
    errors.ts          # Кастомні помилки
    retry.ts           # Retry логіка
    pagination.ts      # Pager для списків
  tests/
    client.test.ts
  dist/                # Скомпільований JS + типи
  package.json
  tsconfig.json
  README.md

HTTP-клієнт

// src/client.ts
export interface MyApiConfig {
  apiKey: string;
  baseUrl?: string;
  timeout?: number;
  maxRetries?: number;
}

export class MyApiError extends Error {
  constructor(
    message: string,
    public readonly statusCode: number,
    public readonly code: string,
    public readonly requestId: string
  ) {
    super(message);
    this.name = 'MyApiError';
  }
}

export class MyApiClient {
  private readonly baseUrl: string;
  private readonly apiKey: string;
  private readonly timeout: number;
  private readonly maxRetries: number;

  // Ресурси
  public readonly users: UsersResource;
  public readonly projects: ProjectsResource;
  public readonly webhooks: WebhooksResource;

  constructor(config: MyApiConfig) {
    this.baseUrl = config.baseUrl ?? 'https://api.myproduct.com/v1';
    this.apiKey = config.apiKey;
    this.timeout = config.timeout ?? 30_000;
    this.maxRetries = config.maxRetries ?? 3;

    this.users = new UsersResource(this);
    this.projects = new ProjectsResource(this);
    this.webhooks = new WebhooksResource(this);
  }

  async request<T>(
    method: string,
    path: string,
    options: RequestOptions = {}
  ): Promise<T> {
    const url = `${this.baseUrl}${path}`;
    const headers: Record<string, string> = {
      'Authorization': `Bearer ${this.apiKey}`,
      'Content-Type': 'application/json',
      'User-Agent': `myapi-sdk-node/${SDK_VERSION}`,
      'X-SDK-Version': SDK_VERSION,
    };

    let lastError: Error | undefined;

    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      if (attempt > 0) {
        // Exponential backoff: 1s, 2s, 4s
        await new Promise(resolve => setTimeout(resolve, 2 ** (attempt - 1) * 1000));
      }

      try {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.timeout);

        const response = await fetch(url, {
          method,
          headers,
          body: options.body ? JSON.stringify(options.body) : undefined,
          signal: controller.signal,
        });

        clearTimeout(timeoutId);

        const requestId = response.headers.get('x-request-id') ?? 'unknown';

        if (!response.ok) {
          const error = await response.json().catch(() => ({}));

          // Не ретраим клієнтські помилки
          if (response.status < 500) {
            throw new MyApiError(
              error.message ?? 'API Error',
              response.status,
              error.code ?? 'UNKNOWN',
              requestId
            );
          }

          lastError = new MyApiError(
            error.message ?? 'Server Error',
            response.status,
            error.code ?? 'SERVER_ERROR',
            requestId
          );
          continue;
        }

        if (response.status === 204) return undefined as T;
        return response.json();
      } catch (error) {
        if (error instanceof MyApiError) throw error;
        lastError = error as Error;
      }
    }

    throw lastError;
  }
}

Ресурс з пагінацією

// src/resources/projects.ts
export interface Project {
  id: string;
  name: string;
  status: 'active' | 'archived';
  createdAt: string;
}

export interface ListProjectsParams {
  limit?: number;
  cursor?: string;
  status?: 'active' | 'archived';
}

export interface PaginatedResponse<T> {
  data: T[];
  nextCursor?: string;
  hasMore: boolean;
  total: number;
}

export class ProjectsResource {
  constructor(private client: MyApiClient) {}

  async list(params: ListProjectsParams = {}): Promise<PaginatedResponse<Project>> {
    const query = new URLSearchParams();
    if (params.limit) query.set('limit', params.limit.toString());
    if (params.cursor) query.set('cursor', params.cursor);
    if (params.status) query.set('status', params.status);

    return this.client.request('GET', `/projects?${query}`);
  }

  // Auto-paging iterator
  async *listAll(params: Omit<ListProjectsParams, 'cursor'> = {}): AsyncIterable<Project> {
    let cursor: string | undefined;

    do {
      const page = await this.list({ ...params, cursor, limit: params.limit ?? 100 });
      yield* page.data;
      cursor = page.nextCursor;
    } while (cursor);
  }

  async create(data: { name: string; description?: string }): Promise<Project> {
    return this.client.request('POST', '/projects', { body: data });
  }

  async get(id: string): Promise<Project> {
    return this.client.request('GET', `/projects/${id}`);
  }

  async update(id: string, data: Partial<Pick<Project, 'name' | 'status'>>): Promise<Project> {
    return this.client.request('PATCH', `/projects/${id}`, { body: data });
  }

  async delete(id: string): Promise<void> {
    return this.client.request('DELETE', `/projects/${id}`);
  }
}

Верифікація webhook

// src/resources/webhooks.ts
import { createHmac, timingSafeEqual } from 'crypto';

export class WebhooksResource {
  constructor(private client: MyApiClient) {}

  verify(payload: string | Buffer, signature: string, secret: string): boolean {
    const expectedSignature = 'sha256=' + createHmac('sha256', secret)
      .update(payload)
      .digest('hex');

    const sigBuffer = Buffer.from(signature);
    const expectedBuffer = Buffer.from(expectedSignature);

    if (sigBuffer.length !== expectedBuffer.length) return false;
    return timingSafeEqual(sigBuffer, expectedBuffer);
  }

  constructEvent(
    payload: string,
    signature: string,
    secret: string
  ): WebhookEvent {
    if (!this.verify(payload, signature, secret)) {
      throw new Error('Invalid webhook signature');
    }
    return JSON.parse(payload);
  }
}

Публікація в npm

{
  "name": "@mycompany/api-sdk",
  "version": "1.0.0",
  "main": "./dist/index.js",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "files": ["dist"],
  "scripts": {
    "build": "tsup src/index.ts --format cjs,esm --dts",
    "test": "vitest run",
    "prepublishOnly": "npm run build && npm test"
  }
}
// Використання SDK
import { MyApiClient } from '@mycompany/api-sdk';

const api = new MyApiClient({ apiKey: process.env.MYAPI_KEY! });

// Прості операції
const project = await api.projects.create({ name: 'New Project' });

// Auto-paging
for await (const project of api.projects.listAll({ status: 'active' })) {
  console.log(project.name);
}

// Верифікація webhook
const event = api.webhooks.constructEvent(rawBody, signature, webhookSecret);

Розробка TypeScript SDK з авторетраями, пагінацією та публікацією в npm — 3–5 робочих днів.