npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nodash/sdk

v0.2.1

Published

Minimal SDK for nodash - the foundation layer

Readme

@nodash/sdk

The Nodash SDK is the foundation client library for the Nodash analytics ecosystem. It provides a minimal, type-safe interface for event tracking, user identification, and server health monitoring. The SDK is designed for both human developers and AI agents, offering comprehensive functionality with a simple API surface.

Installation

npm install @nodash/sdk

Node.js / CommonJS

const { NodashSDK } = require('@nodash/sdk');

const nodash = new NodashSDK('https://your-server.com', 'your-optional-token');

ES Modules / Web

import { NodashSDK } from '@nodash/sdk';

const nodash = new NodashSDK('https://your-server.com', 'your-optional-token');

Browser (via CDN)

<script type="module">
  import { NodashSDK } from 'https://unpkg.com/@nodash/sdk/dist/index.esm.js';
  
  const nodash = new NodashSDK('https://your-server.com', 'your-optional-token');
</script>

Usage Examples

// Track events
await nodash.track('user_signed_up', { 
  plan: 'premium',
  source: 'website' 
});

// Identify users
await nodash.identify('user-123', {
  name: 'John Doe',
  email: '[email protected]'
});

// Check server health
const health = await nodash.health();
console.log('Server status:', health.status);

Architecture

The Nodash SDK serves as the foundation layer of the Nodash ecosystem:

┌─────────────────┐
│   @nodash/mcp   │  ← AI Agent Layer
│  (AI Agents)    │
└─────────────────┘
         ↑
┌─────────────────┐
│   @nodash/cli   │  ← Developer Layer
│  (Developer)    │
└─────────────────┘
         ↑
┌─────────────────┐
│   @nodash/sdk   │  ← Foundation Layer (this package)
│   (Foundation)  │
└─────────────────┘

The SDK provides:

  • Minimal API Surface: Core methods for tracking, identification, and health monitoring
  • Type Safety: Full TypeScript support with comprehensive type definitions
  • Error Handling: Clear, structured error messages for debugging
  • Multi-tenant Support: Automatic tenant derivation from API tokens

Authentication and Multi-tenancy

API Token Format

The SDK supports different API token formats for various authentication scenarios:

// Single-tenant token (standard format)
const sdk = new NodashSDK('https://api.com', 'sk-your-secret-token');

// Multi-tenant token (tenant auto-derived)
const sdk = new NodashSDK('https://api.com', 'demo-api-key-tenant1');
// Tenant 'tenant1' is automatically extracted from the token

Tenant Derivation

For multi-tenant servers, the SDK automatically derives the tenant from the API token pattern:

  • Token format: {prefix}-{suffix}-{tenant}
  • Example: demo-api-key-tenant1 → tenant: tenant1
  • Example: prod-key-company-abc → tenant: company-abc

Custom Headers

You can provide custom headers for additional authentication or configuration:

const sdk = new NodashSDK('https://api.com', 'your-token', {
  headers: {
    'X-Custom-Auth': 'additional-auth-token',
    'X-Environment': 'production',
    'X-Client-Version': '1.0.0'
  }
});

Environment-based Configuration

const sdk = new NodashSDK(
  process.env.NODASH_URL!,
  process.env.NODASH_TOKEN,
  {
    headers: {
      'X-Environment': process.env.NODE_ENV || 'development'
    }
  }
);

AI Agent Integration

const sdk = new NodashSDK(process.env.NODASH_URL!, process.env.NODASH_TOKEN);

// Track AI interactions with structured data
await sdk.track('ai_interaction', {
  model: 'gpt-4',
  tokens_used: 1337,
  user_satisfaction: 'high',
  response_time_ms: 250
});

API Reference

Constructor

new NodashSDK(baseUrl: string, apiToken?: string)

Parameters:

  • baseUrl (required): Your server URL. Must be a valid URL.
  • apiToken (optional): Authentication token. For multi-tenant servers, tenant information is automatically derived from the token pattern (e.g., demo-api-key-tenant1tenant1).

Example:

// With token (tenant auto-derived for multi-tenant servers)
const sdk = new NodashSDK('https://api.yourserver.com', 'demo-api-key-tenant1');

// With token only (for single-tenant servers)
const sdk = new NodashSDK('https://api.yourserver.com', 'sk-your-secret-token');

// Without token (for servers that don't require authentication)
const sdk = new NodashSDK('https://your-local-server.com');

track(event, properties?)

Track events that happen in your application.

await sdk.track(event: string, properties?: Record<string, any>): Promise<void>

Parameters:

  • event: Event name describing what happened
  • properties: Additional event data (optional)

Examples:

// Simple event
await sdk.track('button_clicked');

// Event with context
await sdk.track('purchase_completed', {
  amount: 99.99,
  currency: 'USD',
  items: ['product_1', 'product_2', 'product_3']
});

identify(userId, traits?)

Identify users and associate traits with them.

await sdk.identify(userId: string, traits?: Record<string, any>): Promise<void>

Parameters:

  • userId: Unique identifier for the user
  • traits: User attributes (optional)

Examples:

// Basic identification
await sdk.identify('user-123');

// With user traits
await sdk.identify('user-456', {
  email: '[email protected]',
  plan: 'pro',
  role: 'developer'
});

health()

Check server health status.

await sdk.health(): Promise<HealthStatus>

Returns:

interface HealthStatus {
  status: 'healthy' | 'degraded' | 'unhealthy';
  version: string;
  uptime: number;
  checks: HealthCheck[];
}

Example:

const health = await sdk.health();
if (health.status === 'healthy') {
  console.log('Server is operational');
} else {
  console.log('Server health check failed');
}

getConfig()

Get the current SDK configuration.

sdk.getConfig(): NodashConfig

Returns:

interface NodashConfig {
  baseUrl: string;
  apiToken?: string;
  environment?: string;
  customHeaders?: Record<string, string>;
}

Example:

const config = sdk.getConfig();
console.log('Base URL:', config.baseUrl);

queryEvents(options?)

Query events with filtering and pagination.

sdk.queryEvents(options?: QueryOptions): Promise<QueryResult>

Parameters:

interface QueryOptions {
  eventTypes?: string[];
  userId?: string;
  startDate?: Date;
  endDate?: Date;
  properties?: Record<string, any>;
  sortBy?: 'timestamp' | 'eventName' | 'userId';
  sortOrder?: 'asc' | 'desc';
  limit?: number;
  offset?: number;
  format?: 'json' | 'csv';
}

Returns:

interface QueryResult {
  events: AnalyticsEvent[];
  totalCount: number;
  hasMore: boolean;
  pagination: PaginationInfo;
  executionTime: number;
}

interface AnalyticsEvent {
  eventId: string;
  tenantId: string;
  userId?: string;
  eventName: string;
  properties: Record<string, any>;
  timestamp: Date;
  receivedAt: Date;
  sessionId?: string;
  deviceId?: string;
}

Example:

const result = await sdk.queryEvents({
  eventTypes: ['user_signup', 'purchase'],
  startDate: new Date('2024-01-01'),
  limit: 100
});

console.log(`Found ${result.totalCount} events`);
result.events.forEach(event => {
  console.log(`${event.eventName} at ${event.timestamp}`);
});

queryUsers(options?)

Query users with filtering and pagination.

sdk.queryUsers(options?: UserQueryOptions): Promise<UserQueryResult>

Parameters:

interface UserQueryOptions {
  userId?: string;
  activeSince?: Date;
  activeUntil?: Date;
  properties?: Record<string, any>;
  sortBy?: 'firstSeen' | 'lastSeen' | 'eventCount' | 'sessionCount';
  sortOrder?: 'asc' | 'desc';
  limit?: number;
  offset?: number;
  format?: 'json' | 'csv';
}

Returns:

interface UserQueryResult {
  users: UserRecord[];
  totalCount: number;
  hasMore: boolean;
  pagination: PaginationInfo;
  executionTime: number;
}

interface UserRecord {
  userId: string;
  tenantId: string;
  properties: Record<string, any>;
  firstSeen: Date;
  lastSeen: Date;
  sessionCount: number;
  eventCount: number;
}

Example:

const result = await sdk.queryUsers({
  activeSince: new Date('2024-01-01'),
  sortBy: 'lastSeen',
  limit: 50
});

console.log(`Found ${result.totalCount} users`);
result.users.forEach(user => {
  console.log(`User ${user.userId}: ${user.eventCount} events`);
});

Server Implementation

To implement a Nodash-compatible server, your server must support the following endpoints:

Your server needs to implement these endpoints:

POST /track

Accept tracking events:

{
  "event": "user_action",
  "properties": { "key": "value" },
  "timestamp": "2024-01-01T00:00:00.000Z"
}

POST /identify

Accept user identification:

{
  "userId": "user-123",
  "traits": { "name": "John Doe" },
  "timestamp": "2024-01-01T00:00:00.000Z"
}

GET /health

Return server status:

{
  "status": "healthy",
  "version": "1.0.0",
  "uptime": 3600,
  "checks": [
    { "name": "database", "status": "pass" },
    { "name": "redis", "status": "pass" }
  ]
}

Error Handling

The SDK provides comprehensive error handling with clear, structured error messages for debugging and troubleshooting.

Constructor Errors

// Invalid baseUrl
try {
  const sdk = new NodashSDK('not-a-url');
} catch (error) {
  console.log(error.message); // "baseUrl must be a valid URL"
}

// Missing baseUrl
try {
  const sdk = new NodashSDK('');
} catch (error) {
  console.log(error.message); // "baseUrl is required and must be a string"
}

// Invalid apiToken
try {
  const sdk = new NodashSDK('https://api.com', '');
} catch (error) {
  console.log(error.message); // "apiToken is required and must be a string"
}

Method Validation Errors

// Track method errors
try {
  await sdk.track('', {}); // Empty event name
} catch (error) {
  console.log(error.message); // "event name is required and must be a string"
}

try {
  await sdk.track(null); // Invalid event type
} catch (error) {
  console.log(error.message); // "event name is required and must be a string"
}

// Identify method errors
try {
  await sdk.identify(''); // Empty userId
} catch (error) {
  console.log(error.message); // "userId is required and must be a string"
}

try {
  await sdk.identify(null); // Invalid userId type
} catch (error) {
  console.log(error.message); // "userId is required and must be a string"
}

Network and Server Errors

try {
  await sdk.track('user_action', { test: true });
} catch (error) {
  if (error.code === 'ECONNREFUSED') {
    console.log('Server is not reachable');
  } else if (error.status === 401) {
    console.log('Authentication failed - check your API token');
  } else if (error.status === 429) {
    console.log('Rate limit exceeded - please retry later');
  } else if (error.status >= 500) {
    console.log('Server error - please try again');
  } else {
    console.log('Request failed:', error.message);
  }
}

Error Response Format

When server requests fail, the SDK throws errors with the following structure:

interface SDKError extends Error {
  status?: number;        // HTTP status code
  code?: string;          // Error code (e.g., 'ECONNREFUSED')
  response?: any;         // Server response body
  requestId?: string;     // Request ID for debugging
}

Common Error Scenarios

| Error | Cause | Solution | |-------|-------|----------| | baseUrl must be a valid URL | Invalid URL format | Ensure URL includes protocol (http/https) | | event name is required | Empty or null event name | Provide a non-empty string for event name | | userId is required | Empty or null userId | Provide a non-empty string for userId | | 401 Unauthorized | Invalid or missing API token | Check your API token configuration | | 429 Too Many Requests | Rate limit exceeded | Implement retry logic with backoff | | ECONNREFUSED | Server not reachable | Check server URL and network connectivity | | ETIMEDOUT | Request timeout | Check network connectivity or increase timeout |

Error Handling Best Practices

import { NodashSDK } from '@nodash/sdk';

const sdk = new NodashSDK(process.env.NODASH_URL, process.env.NODASH_TOKEN);

async function trackEventSafely(event: string, properties?: Record<string, any>) {
  try {
    await sdk.track(event, properties);
  } catch (error) {
    // Log error for debugging
    console.error('Failed to track event:', {
      event,
      error: error.message,
      status: error.status,
      requestId: error.requestId
    });
    
    // Handle specific error types
    if (error.status === 429) {
      // Implement retry with exponential backoff
      await retryWithBackoff(() => sdk.track(event, properties));
    } else if (error.status >= 500) {
      // Queue for retry later
      queueForRetry(event, properties);
    }
    
    // Don't throw - allow application to continue
  }
}

async function retryWithBackoff(fn: () => Promise<any>, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
    }
  }
}

Common Patterns

Usage Examples

Environment-based Configuration

const sdk = new NodashSDK(
  process.env.NODE_ENV === 'production' 
    ? 'https://api.nodash.com'
    : 'http://localhost:3000',
  process.env.NODASH_TOKEN
);

Web Application Integration

import { NodashSDK } from '@nodash/sdk';

const analytics = new NodashSDK('https://api.example.com', 'your-api-token');

// Track page views
await analytics.track('page_view', {
  page: window.location.pathname,
  referrer: document.referrer,
  user_agent: navigator.userAgent
});

// Track user interactions
document.getElementById('signup-button').addEventListener('click', async () => {
  await analytics.track('signup_button_clicked', {
    page: 'landing',
    button_position: 'header'
  });
});

Server-side Integration

import { NodashSDK } from '@nodash/sdk';

const analytics = new NodashSDK(process.env.NODASH_URL, process.env.NODASH_TOKEN);

// Track API usage
app.post('/api/users', async (req, res) => {
  // Create user logic...
  
  await analytics.track('user_created', {
    user_id: newUser.id,
    plan: newUser.plan,
    source: req.headers['x-source'] || 'api'
  });
  
  res.json(newUser);
});

AI Agent Integration

import { NodashSDK } from '@nodash/sdk';

const analytics = new NodashSDK(process.env.NODASH_URL, process.env.NODASH_TOKEN);

// Track AI interactions
async function trackAIInteraction(interaction: AIInteraction) {
  await analytics.track('ai_interaction', {
    model: interaction.model,
    tokens_used: interaction.tokensUsed,
    response_time_ms: interaction.responseTime,
    user_satisfaction: interaction.satisfaction,
    task_type: interaction.taskType
  });
}

// Track agent performance
await analytics.track('agent_task_completed', {
  task_id: 'task-123',
  duration_ms: 1500,
  success: true,
  error_count: 0
});

Testing and Development

import { NodashSDK } from '@nodash/sdk';

const sdk = new NodashSDK('http://localhost:3000');

// Run your test scenarios
await sdk.track('test_event_1', { test: true });
await sdk.identify('test-user', { role: 'tester' });

Batch Operations

Currently, each method makes individual HTTP requests. For batch operations, implement batching logic in your server.

Troubleshooting

Q: My requests are failing with 401 errors A: Check your API token configuration. Verify that your server requires authentication and that you're providing a valid token.

Q: The SDK is throwing "baseUrl must be a valid URL" errors A: Ensure your URL includes the protocol (http:// or https://) and is properly formatted.

Q: Nothing is happening when I call track() A: Check your server logs and network connectivity. Verify that your server is running and accessible at the configured URL.

Q: Can I use this with my custom server? A: Yes, as long as your server implements the required HTTP endpoints (/track, /identify, /health) with the expected request/response formats.

Contributing

Contributions are welcome! Please follow these guidelines:

  • Keep implementations simple and focused
  • Write comprehensive tests for new features
  • Update documentation for any API changes

License

MIT License


Built by the Nodash team