@measuremetrics/measure-sdk
v1.0.1
Published
Official Node.js/TypeScript SDK for the Measure API
Maintainers
Readme
Measure Node.js SDK
Official Node.js client for the Measure API, so you can define custom metrics, record measurements, and query time series analytics from your server-side code.
Installation
bun add @measuremetrics/measure-sdknpm install @measuremetrics/measure-sdkQuick start
import { MeasureClient, QueryBuilder } from '@measuremetrics/measure-sdk';
const client = new MeasureClient({
apiKey: process.env.MEASURE_API_KEY,
organizationId: 'org_123',
environmentId: 'env_456',
});
// Create a metric
const metric = await client.metrics.create({
name: 'Monthly Recurring Revenue',
unit: 'currency',
precision_recorded: 2,
});
// Record a measurement
await client.metricAdjustments.create(metric.id, {
timestamp: '2025-03-01T00:00:00Z',
amount: 5000.00,
});
// Query the data
const result = await client.queries.execute(
new QueryBuilder()
.addMetric(metric.id, 'MRR')
.timeRangePreset('Last30Days')
.granularity('day')
.build()
);Configuration
Pass a MeasureClientConfig object when creating the client:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| apiKey | string | required | API key for authentication |
| baseURL | string | https://measure.dev | Base URL for the API |
| organizationId | string | -- | Organization ID for request context |
| environmentId | string | -- | Environment ID for request context |
| timeout | number | 30000 | Request timeout in milliseconds |
| retry | Partial<RetryConfig> | see below | Retry configuration |
| headers | Record<string, string> | -- | Additional headers for all requests |
| fetch | typeof fetch | -- | Custom fetch implementation |
Resources
Every resource follows the same pattern: list, create, get, update, delete, and search where applicable. Some resources have additional methods.
// List with pagination
const metrics = await client.metrics.listPaginated({ limit: 50 });
// Create
const metric = await client.metrics.create({ name: 'Revenue', unit: 'currency' });
// Get by ID
const metric = await client.metrics.get('met_123');
// Update
await client.metrics.update('met_123', { name: 'Total Revenue' });
// Delete
await client.metrics.delete('met_123');
// Search
const results = await client.metrics.search({ search: { q: 'revenue' } });Available resources
| Resource | Property | Description |
|----------|----------|-------------|
| Annotations | client.annotations | Time-based annotations for dashboards and charts |
| API Keys | client.apiKeys | Manage API keys for authentication |
| Business Reviews | client.businessReviews | Periodic business review snapshots |
| Calculated Metrics | client.calculatedMetrics | Expression-based derived metrics |
| Chat | client.chat | AI-powered metric insights |
| Cohorts | client.cohorts | User and entity cohort definitions |
| Dashboards | client.dashboards | Dashboard configuration and layout |
| Entities | client.entities | Business object types (customers, products, etc.) |
| Entity Members | client.entityMembers | Data instances of entity types |
| Environments | client.environments | Workspace isolation (dev, staging, production) |
| Event Definitions | client.eventDefinitions | Event schema definitions |
| Event Topics | client.eventTopics | Event topic groupings |
| Goals | client.goals | Metric targets and goal tracking |
| Health | client.health | API health check |
| Integrations | client.integrations | Third-party integration configuration |
| Managed Entities | client.managedEntities | System-managed entity types |
| Metric Adjustments | client.metricAdjustments | Record measurements with dimensional context |
| Metrics | client.metrics | Measurable quantities to track over time |
| Narratives | client.narratives | AI-generated metric narratives |
| Organizations | client.organizations | Top-level tenant management |
| People | client.people | User profiles and identity |
| Public API Keys | client.publicApiKeys | Write-only keys for client-side tracking |
| Queries | client.queries | Time series analytics queries |
| System Logs | client.systemLogs | Audit and system event logs |
| Teams | client.teams | Team management within organizations |
| Tracking | client.tracking | High-volume event tracking |
| Users | client.users | User account management |
Querying analytics
Use QueryBuilder to construct time series queries with a fluent API:
import { QueryBuilder } from '@measuremetrics/measure-sdk';
const query = new QueryBuilder()
.addMetric('met_revenue', 'Revenue', 2)
.addYearOverYear('met_revenue', 'Revenue YoY')
.timeRange('2025-01-01', '2025-03-31')
.granularity('month')
.addDimension('ent_customer', 'prop_region', 'Region')
.filterBy('ent_customer', 'prop_tier', 'Equals', { String: 'Enterprise' })
.build();
const result = await client.queries.execute(query);Temporal metric types
| Method | Description |
|--------|-------------|
| addMetric(id, name?, precision?) | Base metric value |
| addYearOverYear(metricId, alias) | Year-over-year comparison |
| addPeriodOverPeriod(metricId, alias, offset) | Custom period comparison |
| addGrowthRate(metricId, alias, offset) | Growth rate calculation |
| addMovingAverage(metricId, alias, granularity, windowSize) | Moving average |
| addCumulative(metricId, alias, fromPeriod) | Cumulative total |
Time range options
// Preset
.timeRangePreset('Last30Days') // Last7Days, ThisMonth, ThisYear, etc.
// Custom dates
.timeRange('2025-01-01', '2025-03-31')Granularity
.granularity('day') // day, week, month, quarter, yearFiltering
Use FilterBuilder to construct type-safe property filters:
import { FilterBuilder } from '@measuremetrics/measure-sdk';
const filters = FilterBuilder.create()
.in('ent_customer', 'prop_region', ['US-West', 'US-East'])
.greaterThan('ent_customer', 'prop_mrr', 1000)
.isNotNull('ent_customer', 'prop_company')
.build();Available operators
| Operator | Method | Value type |
|----------|--------|------------|
| Equals | .equals() | string, number, boolean, Date |
| NotEquals | .notEquals() | string, number, boolean, Date |
| GreaterThan | .greaterThan() | number |
| GreaterThanOrEqual | .greaterThanOrEqual() | number |
| LessThan | .lessThan() | number |
| LessThanOrEqual | .lessThanOrEqual() | number |
| Like | .like() | string (SQL pattern with %) |
| ILike | .ilike() | string (case-insensitive) |
| Contains | .contains() | string |
| IContains | .icontains() | string (case-insensitive) |
| In | .in() | string[] or number[] |
| NotIn | .notIn() | string[] or number[] |
| IsNull | .isNull() | -- |
| IsNotNull | .isNotNull() | -- |
Combine multiple filter builders:
const combined = FilterBuilder.and([builder1, builder2]); // AND logicError handling
The SDK throws typed error classes for different failure scenarios:
import {
MeasureClient,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
isRetryableError,
} from '@measuremetrics/measure-sdk';
try {
await client.metrics.create({ name: 'Revenue', unit: 'currency' });
} catch (error) {
if (error instanceof ValidationError) {
console.log('Invalid input:', error.validationErrors);
} else if (error instanceof AuthenticationError) {
console.log('Bad API key');
} else if (error instanceof NotFoundError) {
console.log('Resource not found');
} else if (error instanceof RateLimitError) {
console.log('Rate limited, retry after:', error.retryAfter);
} else if (isRetryableError(error)) {
console.log('Server error, will retry');
}
}Error classes
| Class | Status | Description |
|-------|--------|-------------|
| BadRequestError | 400 | Malformed request |
| AuthenticationError | 401 | Invalid or missing API key |
| PermissionDeniedError | 403 | Insufficient permissions |
| NotFoundError | 404 | Resource does not exist |
| ConflictError | 409 | Duplicate resource |
| UnprocessableEntityError | 422 | Semantically invalid request |
| ValidationError | 422 | Field-level validation errors |
| RateLimitError | 429 | Rate limit exceeded |
| InternalServerError | 500 | Server error |
| BadGatewayError | 502 | Bad gateway |
| ServiceUnavailableError | 503 | Service temporarily unavailable |
| GatewayTimeoutError | 504 | Gateway timeout |
| APIConnectionError | -- | Connection failed |
| APIConnectionTimeoutError | -- | Request timed out |
| APINetworkError | -- | Network-level failure |
Type guards
isAPIError(error)-- any API errorisClientError(error)-- 4xx errorsisServerError(error)-- 5xx errorsisRetryableError(error)-- 5xx and network errors
Retry behavior
The SDK automatically retries failed requests for retryable errors (408, 429, 500, 502, 503, 504) with exponential backoff.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| maxRetries | number | 3 | Maximum retry attempts |
| initialDelayMs | number | 500 | Delay before first retry |
| maxDelayMs | number | 60000 | Maximum delay between retries |
| jitter | JitterStrategy | 'full' | Jitter strategy |
Jitter strategies
full-- Random delay between 0 and the exponential delay (recommended)equal-- Half deterministic, half randomdecorrelated-- Each retry delay is independent (recommended for high concurrency)none-- Pure exponential backoff
Rate limit errors (429) automatically respect the Retry-After header.
Context switching
Switch organization and environment context without creating a new client:
client.setContext('org_456', 'env_staging');
// All subsequent requests use the new contextTypeScript
The SDK is written in TypeScript with strict mode. All request and response types are exported:
import type {
Metric,
MetricCreateRequest,
Entity,
EntityCreateRequest,
MeasureClientConfig,
RetryConfig,
} from '@measuremetrics/measure-sdk';Dual ESM and CommonJS output is provided.
Requirements
- Node.js >= 18
