JSPM

  • Created
  • Published
  • Downloads 101
  • Score
    100M100P100Q81983F
  • License MIT

Lightweight TypeScript SDK for sending analytics events to Grain's REST API

Package Exports

  • @grainql/analytics-web

Readme

@grainql/analytics-web

A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API with automatic batching, retry logic, and reliable event delivery.

Features

  • 🚀 Zero dependencies - Tiny bundle size
  • 📦 Automatic batching - Efficient event queueing and bulk sending
  • 🔄 Retry logic - Exponential backoff for failed requests
  • 📡 Beacon API - Reliable event delivery on page exit
  • 🔐 Multiple auth strategies - NONE, server-side, and JWT support
  • 📱 Cross-platform - Works in browsers, Node.js, and React Native
  • 🎯 TypeScript first - Full type safety out of the box
  • 👤 Global user tracking - Set user ID once, used for all events
  • 📊 Template events - Pre-built event tracking for common scenarios

Installation

npm install @grainql/analytics-web

Quick Start

Basic Usage (No Authentication)

import { createGrainAnalytics } from '@grainql/analytics-web';

const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'NONE'
});

// Track an event
grain.track('page_view', {
  page: '/home',
  referrer: document.referrer
});

// Track with user ID
grain.track('button_click', {
  button: 'signup',
  userId: 'user123'
});

Global User ID

Set a user ID once and it will be used for all subsequent events:

// Set global user ID in config
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  userId: 'user123' // Global user ID for all events
});

// Or set it after initialization
grain.setUserId('user123');

// All events will now use this user ID
grain.track('page_view', { page: '/dashboard' });

User Properties

Set user properties that can be used for analytics and segmentation:

// Set properties for the current user
await grain.setProperty({
  plan: 'premium',
  status: 'active',
  signupDate: '2024-01-15',
  source: 'web'
});

// Set properties for a specific user
await grain.setProperty({
  plan: 'free',
  lastLogin: new Date().toISOString()
}, { userId: 'user123' });

// Properties are automatically serialized to strings
await grain.setProperty({
  isActive: true,           // Becomes "true"
  count: 42,               // Becomes "42"
  metadata: {              // Becomes JSON string
    source: 'api',
    version: '2.0'
  }
});

Note: You can set up to 4 properties per request, and all values are automatically converted to strings.

Server-Side Authentication

const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'SERVER_SIDE',
  secretKey: 'your-secret-key'
});

JWT Authentication

const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'JWT',
  authProvider: {
    async getToken() {
      // Return your JWT token from Auth0, next-auth, etc.
      return await getAccessToken();
    }
  }
});

Template Events

The SDK provides pre-built event tracking methods for common scenarios:

User Authentication

// Track login
await grain.trackLogin({
  method: 'email',
  success: true,
  rememberMe: true
});

// Track signup
await grain.trackSignup({
  method: 'google',
  source: 'landing_page',
  plan: 'pro',
  success: true
});

E-commerce

// Track checkout
await grain.trackCheckout({
  orderId: 'order_123',
  total: 99.99,
  currency: 'USD',
  items: [
    { id: 'prod_1', name: 'Product 1', price: 49.99, quantity: 1 },
    { id: 'prod_2', name: 'Product 2', price: 50.00, quantity: 1 }
  ],
  paymentMethod: 'credit_card',
  success: true
});

// Track purchase
await grain.trackPurchase({
  orderId: 'order_123',
  total: 99.99,
  currency: 'USD',
  paymentMethod: 'credit_card',
  shipping: 5.99,
  tax: 8.50
});

// Track cart interactions
await grain.trackAddToCart({
  itemId: 'prod_1',
  itemName: 'Product 1',
  price: 49.99,
  quantity: 1,
  currency: 'USD'
});

await grain.trackRemoveFromCart({
  itemId: 'prod_2',
  itemName: 'Product 2',
  price: 50.00,
  quantity: 1
});
// Track page view
await grain.trackPageView({
  page: '/products',
  title: 'Product Catalog',
  referrer: 'https://google.com',
  url: 'https://yoursite.com/products'
});

// Track search
await grain.trackSearch({
  query: 'blue shoes',
  results: 24,
  filters: { category: 'footwear', color: 'blue' },
  sortBy: 'price_asc'
});

API Reference

Configuration Options

interface GrainConfig {
  tenantId: string;              // Required: Your Grain tenant ID
  apiUrl?: string;               // API base URL (default: 'https://api.grainql.com')
  authStrategy?: AuthStrategy;   // 'NONE' | 'SERVER_SIDE' | 'JWT' (default: 'NONE')
  secretKey?: string;            // Required for SERVER_SIDE auth
  authProvider?: AuthProvider;   // Required for JWT auth
  userId?: string;               // Global user ID for all events
  batchSize?: number;            // Events per batch (default: 50)
  flushInterval?: number;        // Auto-flush interval in ms (default: 5000)
  retryAttempts?: number;        // Retry attempts for failed requests (default: 3)
  retryDelay?: number;           // Base retry delay in ms (default: 1000)
  debug?: boolean;               // Enable debug logging (default: false)
}

Core Methods

track(eventName, properties?, options?)

Track a single event:

grain.track('purchase', {
  product_id: 'abc123',
  price: 29.99,
  currency: 'USD'
});

track(event, options?)

Track with a full event object:

grain.track({
  eventName: 'purchase',
  userId: 'user123',
  properties: {
    product_id: 'abc123',
    price: 29.99
  },
  timestamp: new Date()
});

setUserId(userId)

Set global user ID for all subsequent events:

grain.setUserId('user123');

getUserId()

Get current global user ID:

const userId = grain.getUserId(); // Returns 'user123' or null

identify(userId)

Alias for setUserId() - sets user ID for subsequent events:

grain.identify('user123');

setProperty(properties, options?)

Set user properties for analytics and segmentation:

// Set properties for current user
await grain.setProperty({
  plan: 'premium',
  status: 'active',
  signupDate: '2024-01-15'
});

// Set properties for specific user
await grain.setProperty({
  plan: 'free'
}, { userId: 'user123' });

Parameters:

  • properties: Object with up to 4 key-value pairs
  • options.userId: Optional user ID override

flush()

Manually flush all queued events:

await grain.flush();

destroy()

Clean up resources and send remaining events:

grain.destroy();

Template Event Methods

Authentication Events

  • trackLogin(properties?, options?) - Track user login
  • trackSignup(properties?, options?) - Track user signup

E-commerce Events

  • trackCheckout(properties?, options?) - Track checkout process
  • trackPurchase(properties?, options?) - Track completed purchase
  • trackAddToCart(properties?, options?) - Track item added to cart
  • trackRemoveFromCart(properties?, options?) - Track item removed from cart
  • trackPageView(properties?, options?) - Track page views
  • trackSearch(properties?, options?) - Track search queries

Template Event Properties

All template events support their own typed properties:

// Login event properties
interface LoginEventProperties {
  method?: string;           // 'email', 'google', 'facebook', etc.
  success?: boolean;         // Whether login was successful
  errorMessage?: string;     // Error message if login failed
  loginAttempt?: number;     // Attempt number
  rememberMe?: boolean;      // Whether "remember me" was checked
  twoFactorEnabled?: boolean; // Whether 2FA was used
}

// Checkout event properties
interface CheckoutEventProperties {
  orderId?: string;          // Unique order identifier
  total?: number;            // Total amount
  currency?: string;         // Currency code
  items?: Array<{            // Array of items
    id: string;
    name: string;
    price: number;
    quantity: number;
  }>;
  paymentMethod?: string;    // 'credit_card', 'paypal', 'stripe', etc.
  success?: boolean;         // Whether checkout was successful
  errorMessage?: string;     // Error message if checkout failed
  couponCode?: string;       // Applied coupon code
  discount?: number;         // Discount amount
}

Authentication Strategies

NONE

No authentication required. Events are sent directly to the API.

SERVER_SIDE

Use a secret key for server-side authentication:

  • Obtain your secret key from the Grain dashboard
  • Include it in your configuration
  • Events are sent with Authorization: Chase {SECRET} header

JWT

Use JWT tokens for client-side authentication:

  • Configure JWT settings in your Grain tenant
  • Provide an auth provider that returns valid tokens
  • Supports Auth0, next-auth, and other JWT providers

Build Outputs

The package provides multiple build formats:

  • ESM: dist/index.mjs - Modern ES modules
  • CommonJS: dist/index.js - Node.js compatible
  • IIFE: dist/index.global.js - Browser global (window.Grain)
  • Types: dist/index.d.ts - TypeScript definitions

Browser Usage (Script Tag)

<script src="https://unpkg.com/@grainql/analytics-web/dist/index.global.js"></script>
<script>
  const grain = Grain.createGrainAnalytics({
    tenantId: 'your-tenant-id',
    userId: 'user123'
  });
  
  grain.trackPageView({ page: '/home' });
  grain.trackLogin({ method: 'email', success: true });
</script>

Advanced Usage

Custom Auth Provider

class Auth0Provider {
  constructor(private auth0Client) {}
  
  async getToken() {
    return await this.auth0Client.getTokenSilently();
  }
}

const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  authStrategy: 'JWT',
  authProvider: new Auth0Provider(auth0Client)
});

Error Handling

try {
  await grain.track('event', { data: 'value' }, { flush: true });
} catch (error) {
  console.error('Failed to send event:', error);
}

Debug Mode

const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  debug: true // Enables console logging
});

User Session Management

// Set user ID when user logs in
grain.setUserId('user123');

// Track user-specific events
await grain.trackLogin({ method: 'email', success: true });
await grain.trackPageView({ page: '/dashboard' });

// Clear user ID when user logs out
grain.setUserId(null);

Changelog

[1.4.0] - 2024-12-19

Added

  • User Properties API: New setProperty() method for setting user properties
    • Set up to 4 properties per request
    • Automatic string serialization for all values
    • Support for user-specific property overrides
    • Full authentication support (NONE, SERVER_SIDE, JWT)
  • Updated API Endpoints:
    • Events now use /v1/events/{tenant}/multi endpoint
    • Properties use /v1/events/{tenant}/properties endpoint

Changed

  • Updated all event sending to use the new /multi endpoint
  • Enhanced error handling and retry logic for properties API

Technical

  • Added comprehensive test coverage for properties functionality
  • Updated all existing tests to use new endpoint structure
  • Improved TypeScript interfaces for better type safety

[1.3.0] - Previous Release

  • Template events for common analytics scenarios
  • Enhanced user ID management
  • Improved error handling and retry logic
  • Cross-platform compatibility improvements

License

MIT

Support

For questions and support, please visit grainql.com or contact our support team.