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
- Expected Functionality
- Ensuring Backward Compatibility
- Data Format and Type Safety
- HTTP Headers and Rate Limiting
- Error Handling
- Testing Requirements
- Documentation Standards
- Naming Conventions
- 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:
- Release a new API version (e.g.,
/v2/endpoints) - Maintain the old version for at least 12 months
- Provide migration guides and SDK updates
- 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:
- Track rate limit headers from responses
- Implement exponential backoff when rate limited
- Respect the
Retry-Afterheader (orretry_afterin JSON) - 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:
-
Unit tests (80%+ code coverage)
- Test individual functions and methods
- Mock HTTP requests (don't hit real API)
- Test error handling and edge cases
-
Integration tests
- Test against live API (test environment)
- Cover all supported endpoints
- Verify request/response handling
-
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:
-
README.md
- Installation instructions
- Quick start example
- Link to full documentation
- Supported language versions
- License information
-
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
-
CHANGELOG.md
- Follow Keep a Changelog format
- Document all changes (added, changed, deprecated, removed, fixed, security)
- Use Semantic Versioning
-
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-javascriptondrejchrastina-sdk-pythonondrejchrastina-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:
SDKorClient - 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:
-
Complete the Publishing Checklist
- Repository setup and configuration
- Community health files
- CI/CD automation
- Security and testing
-
Follow Naming Conventions
- Repository name
- Package name
- Tagging and versioning
-
Adhere to Maintainer Duties
- Respond to issues within 1 week
- Review pull requests within 2 weeks
- Keep dependencies updated
-
Set up CI/CD
- See platform-specific guides in CI/CD Automation
- Automate testing and releases
- Publish to package managers automatically
-
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?
- Email: ondrej@chrastina.dev
- Discord: Join our community
- Stack Overflow: Tag your question with ``
Found an issue with these guidelines?
- Open an issue on our documentation repository
- Suggest improvements via pull request
API support:
- API Documentation
- API Status Page (customize URL)
Additional Resources
- API Reference (if applicable)
- SDK Examples Repository (if applicable)
- Community SDKs (if you maintain a list)
- Webhook Documentation (if applicable)
Last Updated: 2026-01-31