SDK Development Guidelines

{: .note }

This is an example template for SDK development guidelines. Customize this document to reflect your API requirements, architecture, and best practices. Remove this note before publishing.

Welcome to Ondřej Chrastina's guidelines for SDK developers. These guidelines cover the requirements needed to create SDKs for our APIs and provide a solid starting point for developing SDKs in the framework of your choice.

Table of Contents

  1. Expected Functionality
  2. Ensuring Backward Compatibility
  3. Data Format and Type Safety
  4. HTTP Headers and Rate Limiting
  5. Error Handling
  6. Testing Requirements
  7. Documentation Standards
  8. Naming Conventions
  9. Publishing Requirements

Expected Functionality

Required API Endpoints

{: .note }

Replace this section with your API's core endpoints. List the HTTP methods, endpoint paths, and brief descriptions.

The SDK must support the following API endpoints:

Method Endpoint Description
GET /resource Retrieve a list of resources
GET /resource/{id} Retrieve a specific resource
POST /resource Create a new resource
PUT /resource/{id} Update an existing resource
DELETE /resource/{id} Delete a resource

Example implementation (conceptual):

// List all resources
const resources = await client.getResources();

// Get a specific resource
const resource = await client.getResource('resource-id');

// Create a resource
const newResource = await client.createResource({
  name: 'Example',
  description: 'An example resource'
});

// Update a resource
const updated = await client.updateResource('resource-id', {
  name: 'Updated Example'
});

// Delete a resource
await client.deleteResource('resource-id');

Feature Support

{: .note }

Customize this table to reflect your API's key features. Include authentication, filtering, pagination, webhooks, etc.

The SDK should support the following features:

Feature Description Priority
Authentication API key or OAuth2 token-based authentication Required
Filtering Query parameters for filtering resources (e.g., ?status=active) Required
Pagination Handling paginated responses with cursor or offset-based pagination Required
Sorting Query parameters for sorting results (e.g., ?sort=created_at) Recommended
Webhooks Webhook signature verification for security Recommended
Rate Limiting Respect rate limit headers and implement exponential backoff Required
Caching Optional client-side caching with cache invalidation Optional
Batch Operations Bulk create, update, or delete operations Optional

Authentication

{: .note }

Replace with your authentication mechanism (API key, OAuth2, JWT, etc.)

API Key Authentication (example):

const client = new ondrejchrastinaClient({
  apiKey: 'your-api-key-here'
});

OAuth2 Authentication (example):

const client = new ondrejchrastinaClient({
  accessToken: 'oauth2-access-token',
  refreshToken: 'oauth2-refresh-token'
});

Type Safety

We recommend using statically typed models if supported by your programming language (TypeScript, Java, C#, Swift, etc.). Otherwise, use dynamically typed models with comprehensive runtime validation.

Statically typed example (TypeScript):

interface Resource {
  id: string;
  name: string;
  description?: string;
  createdAt: Date;
  updatedAt: Date;
}

const resource: Resource = await client.getResource('resource-id');

Dynamically typed example (Python):

# Runtime validation using Pydantic or similar
from pydantic import BaseModel
from datetime import datetime

class Resource(BaseModel):
    id: str
    name: str
    description: str | None = None
    created_at: datetime
    updated_at: datetime

resource = client.get_resource('resource-id')
validated = Resource(**resource)

Ensuring Backward Compatibility

{: .note }

Adapt these guidelines to match your API's versioning and compatibility guarantees.

Our API evolves in a backward-compatible manner. As long as SDKs follow these guidelines, changes to the API won't break them.

Non-Breaking Changes

The following API changes are NOT considered breaking and SDKs should handle them gracefully:

  • Adding new properties to JSON responses
  • Changing the order of JSON properties
  • Adding new resource types or endpoints
  • Adding new enum values to existing fields
  • Adding optional query parameters
  • Adding new HTTP headers in responses

How to Handle Non-Breaking Changes

1. Ignore unknown properties:

// Good: Parse only known fields, ignore extras
const { id, name, description } = response;

// Bad: Fail if unexpected fields exist
if (Object.keys(response).length !== 3) {
  throw new Error('Unexpected response structure');
}

2. Don't rely on property order:

// Good: Access properties by name
const id = response.id;
const name = response.name;

// Bad: Access properties by position
const [id, name] = Object.values(response);

3. Handle new enum values:

// Good: Have a fallback for unknown values
const status = response.status;
if (['active', 'inactive', 'pending'].includes(status)) {
  // Handle known statuses
} else {
  // Handle unknown status gracefully
  console.warn(`Unknown status: ${status}`);
}

Breaking Changes

If a breaking change is necessary, we will:

  1. Release a new API version (e.g., /v2/ endpoints)
  2. Maintain the old version for at least 12 months
  3. Provide migration guides and SDK updates
  4. Announce the change 3 months in advance

SDKs should support multiple API versions simultaneously during transition periods.


Data Format and Type Safety

Response Format

{: .note }

Document your API's response structure, content types, and data formats.

All API responses use JSON format with Content-Type: application/json.

Success response structure:

{
  "data": {
    "id": "abc123",
    "name": "Example Resource",
    "created_at": "2026-01-31T12:00:00Z"
  },
  "meta": {
    "request_id": "req_xyz789"
  }
}

List response structure (paginated):

{
  "data": [
    { "id": "1", "name": "Resource 1" },
    { "id": "2", "name": "Resource 2" }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 42,
    "total_pages": 3,
    "next_page": 2
  },
  "meta": {
    "request_id": "req_xyz789"
  }
}

Data Types

{: .note }

Specify the data types used in your API responses and how SDKs should handle them.

Type JSON Type SDK Type (TypeScript example) Notes
String string string UTF-8 encoded
Integer number number No decimal places
Float number number Decimal precision
Boolean boolean boolean true or false
Date/Time string Date ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ)
Array array T[] Ordered list of items
Object object Record<string, T> Key-value pairs
Null null null or undefined Represents absence of value
Enum string 'value1' | 'value2' Fixed set of values

Parsing dates:

// Parse ISO 8601 date strings
const createdAt = new Date(response.created_at);

// Validate date format
if (isNaN(createdAt.getTime())) {
  throw new Error('Invalid date format');
}

Empty Response vs. 404 Error

The API differentiates between "no results" and "not found":

  • Empty list (200 OK): Query succeeded but returned no items

    { "data": [], "pagination": { "total": 0 } }
    
  • Not found (404): Resource doesn't exist

    { "error": { "code": "not_found", "message": "Resource not found" } }
    

SDKs should handle both cases appropriately.


HTTP Headers and Rate Limiting

{: .note }

Specify required headers, custom headers, and rate limiting details.

Required Request Headers

SDKs must send the following headers with each request:

Authorization: Bearer <api-key-or-token>
Content-Type: application/json
User-Agent: ondrejchrastina-sdk-<language>/<version>
X-SDK-Version: <sdk-version>

Example implementation:

const headers = {
  'Authorization': `Bearer ${apiKey}`,
  'Content-Type': 'application/json',
  'User-Agent': `ondrejchrastina-sdk-javascript/1.2.3`,
  'X-SDK-Version': '1.2.3'
};

Analytics and Tracking

To help us understand SDK usage and provide better support, include these headers:

  • X-SDK-Version: SDK version (e.g., 1.2.3)
  • X-SDK-Language: Programming language (e.g., javascript, python, ruby)
  • User-Agent: Full SDK identifier (e.g., ondrejchrastina-sdk-javascript/1.2.3)

This allows us to:

  • Track which SDK versions are in use
  • Identify compatibility issues
  • Provide version-specific support

Rate Limiting

{: .note }

Document your API's rate limits and how SDKs should handle them.

Rate limits:

  • 100 requests per minute per API key
  • 5000 requests per hour per API key

Response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1643673600

429 Too Many Requests response:

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Retry after 60 seconds.",
    "retry_after": 60
  }
}

SDK implementation:

SDKs should:

  1. Track rate limit headers from responses
  2. Implement exponential backoff when rate limited
  3. Respect the Retry-After header (or retry_after in JSON)
  4. Optionally queue requests when approaching limit

Example with exponential backoff:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || Math.pow(2, attempt);
      await sleep(retryAfter * 1000);
      continue;
    }

    return response;
  }

  throw new Error('Max retries exceeded');
}

Error Handling

{: .note }

Document your API's error format and expected SDK error handling.

Error Response Format

All errors return JSON with this structure:

{
  "error": {
    "code": "validation_error",
    "message": "Invalid request parameters",
    "details": [
      {
        "field": "name",
        "issue": "Name is required"
      }
    ],
    "request_id": "req_abc123"
  }
}

HTTP Status Codes

Status Code Meaning SDK Action
200 OK Success Return data
201 Created Resource created Return new resource
204 No Content Success (no data) Return void/null
400 Bad Request Invalid input Throw validation error
401 Unauthorized Invalid or missing auth Throw auth error
403 Forbidden Insufficient permissions Throw permission error
404 Not Found Resource doesn't exist Throw not found error
429 Too Many Requests Rate limit exceeded Retry with backoff
500 Internal Server Error Server error Retry up to 3 times
503 Service Unavailable Temporary downtime Retry with backoff

SDK Error Classes

{: .note }

Suggest error class hierarchy for strongly-typed languages.

SDKs should define specific error types:

// Base error class
class OndrejchrastinaError extends Error {
  constructor(
    message: string,
    public code: string,
    public statusCode: number,
    public requestId?: string
  ) {
    super(message);
  }
}

// Specific error types
class ValidationError extends OndrejchrastinaError {}
class AuthenticationError extends OndrejchrastinaError {}
class PermissionError extends OndrejchrastinaError {}
class NotFoundError extends OndrejchrastinaError {}
class RateLimitError extends OndrejchrastinaError {
  constructor(message: string, public retryAfter: number) {
    super(message, 'rate_limit_exceeded', 429);
  }
}

Usage:

try {
  const resource = await client.getResource('invalid-id');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.error('Resource not found:', error.message);
  } else if (error instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}s`);
  } else {
    console.error('Unexpected error:', error);
  }
}

Testing Requirements

{: .note }

Specify testing expectations for SDK contributions.

Required Tests

All SDKs must include:

  1. Unit tests (80%+ code coverage)

    • Test individual functions and methods
    • Mock HTTP requests (don't hit real API)
    • Test error handling and edge cases
  2. Integration tests

    • Test against live API (test environment)
    • Cover all supported endpoints
    • Verify request/response handling
  3. Backward compatibility tests

    • Ensure SDK handles API responses with extra fields
    • Test with different API versions if supported

Example Test Structure

// Unit test (mocked)
describe('Client.getResource', () => {
  it('should fetch a resource by ID', async () => {
    // Mock HTTP response
    fetchMock.mockResponseOnce(JSON.stringify({
      data: { id: '123', name: 'Test' }
    }));

    const resource = await client.getResource('123');

    expect(resource.id).toBe('123');
    expect(resource.name).toBe('Test');
  });

  it('should throw NotFoundError for invalid ID', async () => {
    fetchMock.mockResponseOnce('', { status: 404 });

    await expect(client.getResource('invalid'))
      .rejects.toThrow(NotFoundError);
  });
});

// Integration test (real API)
describe('Client Integration', () => {
  it('should create and retrieve a resource', async () => {
    const created = await client.createResource({
      name: 'Integration Test'
    });

    const retrieved = await client.getResource(created.id);

    expect(retrieved.name).toBe('Integration Test');
  });
});

Continuous Integration

Set up CI to run tests automatically:

  • Run on every pull request
  • Run on multiple language versions (e.g., Node 18, 20, 22)
  • Run on multiple operating systems (Linux, macOS, Windows)
  • Report code coverage (use Codecov or Coveralls)

See our CI/CD guides for platform-specific examples.


Documentation Standards

{: .note }

Specify documentation requirements for SDK repositories.

Required Documentation

Every SDK repository must include:

  1. README.md

    • Installation instructions
    • Quick start example
    • Link to full documentation
    • Supported language versions
    • License information
  2. API Documentation

    • Auto-generated from code comments (JSDoc, RDoc, Javadoc, etc.)
    • Hosted on platform-specific docs site (e.g., docs.rs for Rust)
    • All public methods documented with:
      • Description of functionality
      • Parameter types and descriptions
      • Return type and description
      • Example usage
      • Possible exceptions/errors
  3. CHANGELOG.md

    • Follow Keep a Changelog format
    • Document all changes (added, changed, deprecated, removed, fixed, security)
    • Use Semantic Versioning
  4. CONTRIBUTING.md

    • Link to this guidelines document
    • Local development setup
    • How to run tests
    • Code style requirements

Documentation Example

README.md:

# Ondřej Chrastina SDK for JavaScript

Official JavaScript SDK for Ondřej Chrastina API.

## Installation

```bash
npm install @ondrejchrastina/sdk-javascript

Quick Start

const { OndrejchrastinaClient } = require('@ondrejchrastina/sdk-javascript');

const client = new OndrejchrastinaClient({
  apiKey: 'your-api-key'
});

const resources = await client.getResources();
console.log(resources);

Documentation

Full documentation: https://docs.ondrejchrastina.com/sdk/javascript

License

MIT


**Code comments (JSDoc example):**

```javascript
/**
 * Retrieves a resource by its unique identifier.
 *
 * @param {string} id - The resource ID
 * @returns {Promise<Resource>} The requested resource
 * @throws {NotFoundError} If the resource doesn't exist
 * @throws {AuthenticationError} If the API key is invalid
 *
 * @example
 * const resource = await client.getResource('abc123');
 * console.log(resource.name);
 */
async getResource(id) {
  // Implementation
}

Naming Conventions

{: .note }

Customize these recommendations to match your organization's preferences.

Repository Name

Use this pattern: ondrejchrastina-sdk-<language>

Examples:

  • ondrejchrastina-sdk-javascript
  • ondrejchrastina-sdk-python
  • ondrejchrastina-sdk-ruby

Package Name

Follow the conventions of each ecosystem:

Language Pattern Example
JavaScript (npm) @ondrejchrastina/sdk-<language> @ondrejchrastina/sdk-javascript
Python (PyPI) ondrejchrastina-sdk ondrejchrastina-sdk
Ruby (RubyGems) ondrejchrastina_sdk ondrejchrastina_sdk
Java (Maven) com.ondrejchrastina.sdk com.ondrejchrastina.sdk
C# (NuGet) OndřejChrastina.SDK OndřejChrastina.SDK
Go (module) github.com/Simply007/ondrejchrastina-sdk-go Full path
Rust (crates.io) ondrejchrastina_sdk ondrejchrastina_sdk

Class and Module Names

Follow language conventions:

  • PascalCase: OndrejchrastinaClient, ResourceManager
  • camelCase: getResource, createResource
  • snake_case: get_resource, create_resource (Python, Ruby)

Namespace/Module Structure

  • Main namespace: Ondřej Chrastina
  • Sub-namespace: SDK or Client
  • Platform-specific separator (., ::, /, etc.)

Examples:

  • JavaScript: @ondrejchrastina/sdk
  • Python: ondrejchrastina_sdk
  • Ruby: Ondrejchrastina::SDK
  • C#: OndřejChrastina.SDK

Publishing Requirements

{: .note }

Link to your organization's publishing checklist and requirements.

Before your SDK can be officially listed on our website and documentation:

  1. Complete the Publishing Checklist

    • Repository setup and configuration
    • Community health files
    • CI/CD automation
    • Security and testing
  2. Follow Naming Conventions

    • Repository name
    • Package name
    • Tagging and versioning
  3. Adhere to Maintainer Duties

    • Respond to issues within 1 week
    • Review pull requests within 2 weeks
    • Keep dependencies updated
  4. Set up CI/CD

    • See platform-specific guides in CI/CD Automation
    • Automate testing and releases
    • Publish to package managers automatically
  5. Submit for Review

    • Email ondrej@chrastina.dev with repository link
    • Include brief description and supported features
    • We'll review within 2 weeks

Getting Help

Questions about SDK development?

Found an issue with these guidelines?

  • Open an issue on our documentation repository
  • Suggest improvements via pull request

API support:


Additional Resources


Last Updated: 2026-01-31