@zaplink/core
v0.2.1
Published
Core OpenAPI-driven client for Zaplink API
Readme
@zaplink/core
Core OpenAPI-driven client for the Zaplink API. This package provides low-level, typed request builders that work across all JavaScript runtimes (browsers, Node.js, Edge, React Native).
Installation
npm install @zaplink/core
# or
pnpm add @zaplink/core
# or
yarn add @zaplink/coreFeatures
- OpenAPI-driven - Types generated from API specifications
- Runtime-agnostic - Works in browsers, Node.js, Edge, and React Native
- Fetch-based - Uses standard Fetch API (custom fetch can be provided)
- Type-safe - Full TypeScript support with generated types
- Error handling - RFC 7807 Problem Details support
- Idempotency - Built-in idempotency key support
- Rate limiting - Automatic rate limit header parsing
- Retries - Helper methods for retry logic
Usage
Basic Client
import { createClient } from '@zaplink/core';
// Server-side with API key
const client = createClient({
apiKey: process.env.ZAPLINK_SECRET_KEY,
version: '2025-10',
});
// Browser-side with public key
const client = createClient({
publicKey: import.meta.env.VITE_ZAPLINK_PUBLIC_KEY,
});
// With access token (authenticated user)
const client = createClient({
accessToken: userSession.accessToken,
});Making Requests
// GET request
const { data, metadata } = await client.get('/v1/health');
console.log(data); // { status: 'ok', version: '1.0.0' }
console.log(metadata.requestId); // 'req_abc123'
// POST request with body
const { data } = await client.post('/v1/auth/login/start', {
phone: '+5215555555555',
locale: 'es-MX',
});
// With idempotency key
const { data } = await client.post(
'/v1/auth/login/start',
{ phone: '+5215555555555' },
{ idempotencyKey: 'unique-key-123' }
);
// With query parameters
const { data } = await client.get('/v1/projects', {
page: 1,
limit: 10,
});Error Handling
import { HttpError, isHttpError } from '@zaplink/core';
try {
await client.post('/v1/auth/login/verify', {
attemptId: 'attempt_123',
otp: '123456',
});
} catch (error) {
if (isHttpError(error)) {
console.error(`Error ${error.code}: ${error.message}`);
console.error(`Status: ${error.status}`);
console.error(`Request ID: ${error.requestId}`);
// Check for specific error codes
if (error.hasCode('otp_invalid')) {
console.error('Invalid OTP provided');
} else if (error.hasCode('otp_expired')) {
console.error('OTP has expired');
}
} else {
// Network or other errors
console.error('Unexpected error:', error);
}
}Custom Fetch Implementation
import { createClient } from '@zaplink/core';
import { fetch } from 'undici'; // Node.js < 18
const client = createClient({
fetch: fetch as any,
apiKey: process.env.ZAPLINK_SECRET_KEY,
});Response Metadata
const { data, metadata, response } = await client.get('/v1/health');
// Rate limit information
console.log(metadata.rateLimit.remaining); // 99
console.log(metadata.rateLimit.reset); // Unix timestamp
// Request tracking
console.log(metadata.requestId); // 'req_abc123'
// API version
console.log(metadata.apiVersion); // '2025-10'
// Retry information
if (metadata.retryAfter) {
console.log(`Retry after ${metadata.retryAfter} seconds`);
}Configuration Options
interface ClientOptions {
baseUrl?: string; // Default: 'https://api.zaplink.so'
fetch?: FetchLike; // Custom fetch implementation
apiKey?: string; // Secret key (server-side)
publicKey?: string; // Public key (browser-side)
accessToken?: string; // User session token
headers?: Record<string, string>; // Default headers
version?: string; // API version (e.g., '2025-10')
timeout?: number; // Request timeout in ms (default: 30000)
debug?: boolean; // Enable debug logging
}OpenAPI Type Generation
This package uses openapi-typescript to generate TypeScript types from the Zaplink API OpenAPI specifications.
Generating Types
# Generate public API types
pnpm run codegen:public
# Generate admin API types
pnpm run codegen:admin
# Generate both
pnpm run codegenUsing Generated Types
import type { PublicPaths, AdminPaths } from '@zaplink/core';
// Use path types
type LoginStartRequest =
PublicPaths['/v1/auth/login/start']['post']['requestBody']['content']['application/json'];
type LoginStartResponse =
PublicPaths['/v1/auth/login/start']['post']['responses'][200]['content']['application/json'];Error Types
- HttpError - API errors with RFC 7807 Problem Details
- NetworkError - Network/connection failures
- TimeoutError - Request timeout
- ConfigurationError - Invalid client configuration
Best Practices
Use appropriate authentication
- Server:
apiKey - Browser:
publicKey - Authenticated user:
accessToken
- Server:
Handle rate limits
const { metadata } = await client.get('/v1/endpoint'); if (metadata.rateLimit.remaining < 10) { console.warn('Rate limit approaching'); }Implement retry logic
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> { for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { if (isHttpError(error) && error.status >= 500 && i < maxRetries - 1) { await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); continue; } throw error; } } throw new Error('Max retries exceeded'); }Use idempotency keys for POST requests
import { generateIdempotencyKey } from '@zaplink/utils'; await client.post('/v1/endpoint', data, { idempotencyKey: generateIdempotencyKey(), });
License
MIT
