npm package: @metrixly/sdk-v0
Description: TypeScript SDK for Metrixly performance monitoring
Author: V.Zhukovskyi
Latest version: 0.0.3

# Metrixly TypeScript SDK

A lightweight, framework-agnostic performance monitoring library for JavaScript/TypeScript applications. Monitor API endpoint performance and distributed traces with automatic instrumentation for fetch and axios, plus manual tracing capabilities.

## Features

- 🚀 **Automatic Instrumentation** - Zero-config monitoring for fetch and axios HTTP clients
- 📊 **Manual Tracing** - Create custom traces with spans for business logic and operations
- 🔄 **Efficient Batching** - Smart batching with configurable thresholds and automatic flushing
- ⚡ **Retry Logic** - Exponential backoff with jitter for reliable data delivery
- 📦 **Tree-Shakeable** - Optimized for modern bundlers with minimal overhead
- 🌐 **Multi-Environment** - Works seamlessly in Node.js, browsers, and edge runtimes
- 💪 **TypeScript First** - Comprehensive type definitions with excellent IDE support
- 🎯 **Framework Agnostic** - Use with Next.js, Express, React, Vue, or any JavaScript framework

## Installation

```bash
# npm
npm install @metrixly/sdk

# yarn
yarn add @metrixly/sdk

# pnpm
pnpm add @metrixly/sdk

# bun
bun add @metrixly/sdk
```

## Quick Start

### Basic Setup

```typescript
import { MetrixlyClient } from '@metrixly/sdk'

// Initialize the client with your backend URL and API key
const metrixly = new MetrixlyClient({
  baseUrl: 'https://metrixly.example.com',
  apiKey: 'your-api-key-from-dashboard'
})

// Enable automatic fetch instrumentation
metrixly.enableFetchInstrumentation()

// All fetch calls are now automatically monitored
await fetch('https://api.example.com/users')
```

### Automatic Fetch Instrumentation

The SDK automatically captures all fetch requests when instrumentation is enabled:

```typescript
import { MetrixlyClient } from '@metrixly/sdk'

const metrixly = new MetrixlyClient({
  baseUrl: 'https://metrixly.example.com',
  apiKey: 'your-api-key'
})

// Enable fetch instrumentation
metrixly.enableFetchInstrumentation()

// These requests are automatically tracked
const users = await fetch('https://api.example.com/users')
const posts = await fetch('https://api.example.com/posts')

// Disable when needed
metrixly.disableFetchInstrumentation()
```

### Automatic Axios Instrumentation

For applications using axios, enable instrumentation per axios instance:

```typescript
import axios from 'axios'
import { MetrixlyClient } from '@metrixly/sdk'

const metrixly = new MetrixlyClient({
  baseUrl: 'https://metrixly.example.com',
  apiKey: 'your-api-key'
})

// Create your axios instance
const api = axios.create({
  baseURL: 'https://api.example.com'
})

// Enable instrumentation for this instance
metrixly.enableAxiosInstrumentation(api)

// All requests with this instance are now tracked
await api.get('/users')
await api.post('/posts', { title: 'Hello' })

// Disable when needed
metrixly.disableAxiosInstrumentation(api)
```

### Manual Tracing

Create custom traces to monitor business logic and operations:

```typescript
import {MetrixlyClient} from '@metrixly/sdk'

const metrixly = new MetrixlyClient({
    baseUrl: 'https://metrixly.example.com',
    apiKey: 'your-api-key'
})

// Start a trace for a complex operation
const trace = metrixly.startTrace('process-order', {
    orderId: '12345',
    userId: 'user-789'
})

// Add spans for individual steps
trace.addSpan('validate-order', {items: 3})
trace.addSpan('check-inventory')
trace.addSpan('charge-payment', {amount: 99.99})
trace.addSpan('send-confirmation')

// Complete the trace
trace.end('completed', {totalAmount: 99.99})
```

### Async Operations with Tracing

Track async operations by measuring durations:

```typescript
const trace = metrixly.startTrace('user-signup')

// Measure validation

await validateUserInput(userData)
trace.addSpan('validate-input')

// Measure database operation
await createUserInDatabase(userData)
trace.addSpan('create-user')

// Measure email sending
await sendWelcomeEmail(userData.email)
trace.addSpan('send-email')

// Complete the trace
trace.end('completed', { userId: newUser.id })
```

## Configuration Options

All configuration options with their defaults:

```typescript
const metrixly = new MetrixlyClient({
  // Required
  baseUrl: 'https://metrixly.example.com',  // Your Metrixly backend URL
  apiKey: 'your-api-key',                   // API key from dashboard
  
  // Optional
  batchSize: 10,                            // Number of items to batch before sending
  flushInterval: 30000,                     // Time in ms between automatic flushes (30s)
  maxRetries: 3,                            // Maximum retry attempts for failed requests
  retryDelay: 1000,                         // Base delay in ms for retry backoff (1s)
  enableAutoInstrumentation: true,          // Auto-enable fetch instrumentation
  debug: false,                             // Enable debug logging
  logger: console                           // Custom logger implementation
})
```

### Configuration Details

#### `baseUrl` (required)
The URL where your Metrixly backend is deployed. Must be a valid URL.

```typescript
baseUrl: 'https://metrixly.example.com'
```

#### `apiKey` (required)
Your API key from the Metrixly dashboard. Used to authenticate requests and identify your project/application.

```typescript
apiKey: 'mtx_1234567890abcdef'
```

#### `batchSize` (optional, default: 10)
Number of metrics or traces to accumulate before automatically sending to the backend. Lower values mean more frequent network requests but lower latency. Higher values reduce network overhead.

```typescript
batchSize: 20  // Send every 20 items
```

#### `flushInterval` (optional, default: 30000)
Time in milliseconds between automatic flushes. Even if the batch size isn't reached, data will be sent at this interval.

```typescript
flushInterval: 60000  // Flush every 60 seconds
```

#### `maxRetries` (optional, default: 3)
Maximum number of retry attempts for failed network requests. Uses exponential backoff with jitter.

```typescript
maxRetries: 5  // Retry up to 5 times
```

#### `retryDelay` (optional, default: 1000)
Base delay in milliseconds for retry backoff. Actual delay increases exponentially: `delay * (2 ^ attempt)`.

```typescript
retryDelay: 2000  // Start with 2s delay
```

#### `enableAutoInstrumentation` (optional, default: true)
Automatically enable fetch instrumentation on initialization. Set to `false` if you want to manually control when instrumentation starts.

```typescript
enableAutoInstrumentation: false  // Don't auto-enable
```

#### `debug` (optional, default: false)
Enable debug logging to see detailed SDK operations. Useful for troubleshooting.

```typescript
debug: true  // Enable debug logs
```

#### `logger` (optional, default: console)
Custom logger implementation. Must implement the `Logger` interface with `debug`, `info`, `warn`, and `error` methods.

```typescript
// Custom logger example
const customLogger = {
  debug: (msg, ...args) => { /* custom debug */ },
  info: (msg, ...args) => { /* custom info */ },
  warn: (msg, ...args) => { /* custom warn */ },
  error: (msg, ...args) => { /* custom error */ }
}

const metrixly = new MetrixlyClient({
  baseUrl: 'https://metrixly.example.com',
  apiKey: 'your-api-key',
  logger: customLogger
})
```

## API Reference

### MetrixlyClient

Main SDK class for monitoring and tracing.

#### Constructor

```typescript
new MetrixlyClient(config: MetrixlyConfig)
```

Creates a new Metrixly client instance. Throws `MetrixlyError` if configuration is invalid.

#### Methods

##### `enableFetchInstrumentation()`

Enable automatic instrumentation for the global `fetch` function.

```typescript
metrixly.enableFetchInstrumentation()
```

##### `disableFetchInstrumentation()`

Disable fetch instrumentation and restore original fetch behavior.

```typescript
metrixly.disableFetchInstrumentation()
```

##### `enableAxiosInstrumentation(axiosInstance)`

Enable instrumentation for a specific axios instance.

```typescript
const api = axios.create({ baseURL: 'https://api.example.com' })
metrixly.enableAxiosInstrumentation(api)
```

##### `disableAxiosInstrumentation(axiosInstance)`

Disable instrumentation for a specific axios instance.

```typescript
metrixly.disableAxiosInstrumentation(api)
```

##### `startTrace(name, metadata?)`

Start a new manual trace.

```typescript
const trace = metrixly.startTrace('operation-name', {
  userId: '123',
  customField: 'value'
})
```

**Parameters:**
- `name` (string): Name of the trace
- `metadata` (object, optional): Additional context data

**Returns:** `Trace` instance

##### `flush()`

Manually flush all buffered metrics and traces. Returns a promise that resolves when complete.

```typescript
await metrixly.flush()
```

**Returns:** `Promise<void>`

##### `destroy()`

Clean up resources and disable all instrumentation. Call before application shutdown.

```typescript
metrixly.destroy()
```

### Trace

Interface for manual tracing operations.

#### Properties

- `id` (string, readonly): Unique trace identifier
- `name` (string, readonly): Name of the trace
- `startTime` (Date, readonly): When the trace started

#### Methods

##### `addSpan(name, duration, metadata?)`

Add a span to the trace representing an operation.

```typescript
trace.addSpan('database-query', 150, {
  query: 'SELECT * FROM users',
  rows: 42
})
```

**Parameters:**
- `name` (string): Name of the span/operation
- `duration` (number): Duration in milliseconds
- `metadata` (object, optional): Additional context data

##### `end(status, metadata?)`

Complete the trace and send it to the backend.

```typescript
trace.end('completed', { result: 'success' })
// or
trace.end('failed', { error: 'timeout' })
```

**Parameters:**
- `status` ('completed' | 'failed'): Final status of the trace
- `metadata` (object, optional): Additional context data

## Framework Integration

### Next.js

```typescript
// lib/metrixly.ts
import { MetrixlyClient } from '@metrixly/sdk'

export const metrixly = new MetrixlyClient({
  baseUrl: process.env.NEXT_PUBLIC_METRIXLY_URL!,
  apiKey: process.env.NEXT_PUBLIC_METRIXLY_API_KEY!
})

// Enable fetch instrumentation
metrixly.enableFetchInstrumentation()
```

```typescript
// app/api/users/route.ts
import { metrixly } from '@/lib/metrixly'

export async function GET() {
  const trace = metrixly.startTrace('fetch-users')
  
  try {
    const users = await fetchUsers()
    trace.addSpan('fetch-users')
    trace.end('completed')
    
    return Response.json(users)
  } catch (error) {
    trace.end('failed', { error: error.message })
    throw error
  }
}
```

### Express.js

```typescript
// server.ts
import express from 'express'
import { MetrixlyClient } from '@metrixly/sdk'

const app = express()

const metrixly = new MetrixlyClient({
  baseUrl: process.env.METRIXLY_URL,
  apiKey: process.env.METRIXLY_API_KEY
})

metrixly.enableFetchInstrumentation()

// Middleware to trace requests
app.use((req, res, next) => {
  const trace = metrixly.startTrace(`${req.method} ${req.path}`)
  
  res.on('finish', () => {
    trace.end(res.statusCode < 400 ? 'completed' : 'failed', {
      statusCode: res.statusCode,
      method: req.method,
      path: req.path
    })
  })
  
  next()
})

// Graceful shutdown
process.on('SIGTERM', async () => {
  await metrixly.flush()
  metrixly.destroy()
  process.exit(0)
})
```

### React

```typescript
// hooks/useMetrixly.ts
import { useEffect } from 'react'
import { metrixly } from '@/lib/metrixly'

export function useMetrixly() {
  useEffect(() => {
    // Enable instrumentation when app mounts
    metrixly.enableFetchInstrumentation()
    
    return () => {
      // Clean up on unmount
      metrixly.disableFetchInstrumentation()
    }
  }, [])
  
  return metrixly
}
```

```typescript
// components/UserList.tsx
import { useMetrixly } from '@/hooks/useMetrixly'

export function UserList() {
  const metrixly = useMetrixly()
  
  const loadUsers = async () => {
    const trace = metrixly.startTrace('load-users')
    
    try {
      const response = await fetch('/api/users')
      const users = await response.json()
      trace.addSpan('fetch-users')
      trace.end('completed', { count: users.length })
      return users
    } catch (error) {
      trace.end('failed', { error: error.message })
      throw error
    }
  }
  
  // ... rest of component
}
```

## Troubleshooting

### Metrics not appearing in dashboard

**Check your configuration:**
- Verify `baseUrl` is correct and accessible
- Verify `apiKey` is valid and matches your project
- Check network connectivity to your Metrixly backend

**Enable debug logging:**
```typescript
const metrixly = new MetrixlyClient({
  baseUrl: 'https://metrixly.example.com',
  apiKey: 'your-api-key',
  debug: true  // Enable debug logs
})
```

**Check browser console or server logs** for error messages.

### Fetch instrumentation not working

**Ensure instrumentation is enabled:**
```typescript
metrixly.enableFetchInstrumentation()
```

**Check if fetch is being called after instrumentation is enabled.** Instrumentation only affects fetch calls made after `enableFetchInstrumentation()` is called.

**Verify fetch is available** in your environment. The SDK requires native fetch support.

### Axios instrumentation not working

**Ensure you're passing the axios instance:**
```typescript
const api = axios.create({ baseURL: 'https://api.example.com' })
metrixly.enableAxiosInstrumentation(api)  // Pass the instance
```

**Instrumentation is per-instance.** If you have multiple axios instances, enable instrumentation for each one.

**Check axios version.** The SDK requires axios ^1.0.0.

### High memory usage

**Reduce batch size** to send data more frequently:
```typescript
const metrixly = new MetrixlyClient({
  baseUrl: 'https://metrixly.example.com',
  apiKey: 'your-api-key',
  batchSize: 5,  // Smaller batches
  flushInterval: 10000  // Flush more frequently (10s)
})
```

**Call flush() periodically** in long-running applications:
```typescript
setInterval(async () => {
  await metrixly.flush()
}, 60000)  // Flush every minute
```

### TypeScript errors

**Ensure you have TypeScript 5.0+** installed:
```bash
npm install -D typescript@^5.0.0
```

**Check your tsconfig.json** includes proper module resolution:
```json
{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "esModuleInterop": true
  }
}
```

### Data not sending on application shutdown

**Always flush before shutdown:**
```typescript
// Node.js
process.on('SIGTERM', async () => {
  await metrixly.flush()
  metrixly.destroy()
  process.exit(0)
})

// Browser (before unload)
window.addEventListener('beforeunload', async () => {
  await metrixly.flush()
})
```

### CORS errors in browser

**Ensure your Metrixly backend allows CORS** from your application's origin. Configure CORS headers on your backend:

```
Access-Control-Allow-Origin: https://your-app.com
Access-Control-Allow-Headers: X-Api-Key, Content-Type
```

### Network errors and retries

The SDK automatically retries failed requests with exponential backoff. Check logs for retry attempts:

```typescript
const metrixly = new MetrixlyClient({
  baseUrl: 'https://metrixly.example.com',
  apiKey: 'your-api-key',
  maxRetries: 5,  // Increase retries
  retryDelay: 2000,  // Increase initial delay
  debug: true  // See retry attempts in logs
})
```

## Best Practices

### 1. Initialize once, use everywhere

Create a single client instance and export it:

```typescript
// lib/metrixly.ts
export const metrixly = new MetrixlyClient({ ... })
```

### 2. Flush before shutdown

Always flush buffered data before your application exits:

```typescript
process.on('SIGTERM', async () => {
  await metrixly.flush()
  metrixly.destroy()
})
```

### 3. Use meaningful trace names

Choose descriptive names that help identify operations:

```typescript
// Good
metrixly.startTrace('user-registration')
metrixly.startTrace('payment-processing')

// Avoid
metrixly.startTrace('operation')
metrixly.startTrace('trace1')
```

### 4. Add context with metadata

Include relevant information in metadata:

```typescript
const trace = metrixly.startTrace('process-order', {
  orderId: order.id,
  userId: user.id,
  amount: order.total
})
```

### 5. Handle errors in traces

Always complete traces, even on errors:

```typescript
const trace = metrixly.startTrace('api-call')
try {
  await someOperation()
  trace.end('completed')
} catch (error) {
  trace.end('failed', { error: error.message })
  throw error
}
```

### 6. Don't over-instrument

Focus on critical paths and operations. Too much instrumentation can impact performance.

### 7. Use appropriate batch sizes

Balance between network overhead and data freshness:
- High-traffic apps: Larger batch sizes (20-50)
- Low-traffic apps: Smaller batch sizes (5-10)

## Development

```bash
# Install dependencies
bun install

# Run tests
bun test

# Run tests in watch mode
bun test:watch

# Build
bun run build

# Watch mode for development
bun run dev

# Lint
bun run lint

# Format code
bun run format
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

MIT

## Support

For issues and questions:
- GitHub Issues: [https://github.com/yourusername/metrixly/issues](https://github.com/yourusername/metrixly/issues)
- Documentation: [https://metrixly.example.com/docs](https://metrixly.example.com/docs)