@datalyr/api 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,195 +1,391 @@
1
1
  # @datalyr/api
2
2
 
3
- Official API SDK for Datalyr server-side tracking with identity resolution support.
3
+ Server-side analytics and attribution SDK for Node.js. Track events, identify users, and preserve attribution data from your backend.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [How It Works](#how-it-works)
10
+ - [Configuration](#configuration)
11
+ - [Event Tracking](#event-tracking)
12
+ - [Custom Events](#custom-events)
13
+ - [Page Views](#page-views)
14
+ - [User Identity](#user-identity)
15
+ - [Anonymous ID](#anonymous-id)
16
+ - [Identifying Users](#identifying-users)
17
+ - [Groups](#groups)
18
+ - [Attribution Preservation](#attribution-preservation)
19
+ - [Event Queue](#event-queue)
20
+ - [Framework Examples](#framework-examples)
21
+ - [Express.js](#expressjs)
22
+ - [Stripe Webhooks](#stripe-webhooks)
23
+ - [TypeScript](#typescript)
24
+ - [Troubleshooting](#troubleshooting)
25
+ - [License](#license)
26
+
27
+ ---
4
28
 
5
29
  ## Installation
6
30
 
7
31
  ```bash
8
32
  npm install @datalyr/api
9
- # or
10
- yarn add @datalyr/api
11
- # or
12
- pnpm add @datalyr/api
13
33
  ```
14
34
 
35
+ ---
36
+
15
37
  ## Quick Start
16
38
 
17
39
  ```javascript
18
- const { Datalyr } = require('@datalyr/api');
19
- // or
20
40
  import { Datalyr } from '@datalyr/api';
21
41
 
22
- // Initialize with your API key
23
- const datalyr = new Datalyr('your_api_key_here');
42
+ // Initialize
43
+ const datalyr = new Datalyr('dk_your_api_key');
44
+
45
+ // Track events
46
+ await datalyr.track('user_123', 'button_clicked', { button: 'signup' });
47
+
48
+ // Identify users
49
+ await datalyr.identify('user_123', { email: 'user@example.com' });
50
+
51
+ // Clean up on shutdown
52
+ await datalyr.close();
53
+ ```
54
+
55
+ ---
56
+
57
+ ## How It Works
58
+
59
+ The SDK collects events and sends them to the Datalyr backend for analytics and attribution.
60
+
61
+ ### Data Flow
62
+
63
+ 1. Events are created with `track()`, `identify()`, `page()`, or `group()`
64
+ 2. Events are queued locally and sent in batches
65
+ 3. Batches are sent when queue reaches 20 events or every 10 seconds
66
+ 4. Failed requests are retried with exponential backoff
67
+ 5. Events are processed server-side for analytics and attribution reporting
68
+
69
+ ### Event Payload
70
+
71
+ Every event includes:
72
+
73
+ ```javascript
74
+ {
75
+ event: 'purchase', // Event name
76
+ properties: { ... }, // Custom properties
77
+
78
+ // Identity
79
+ anonymous_id: 'uuid', // Persistent ID
80
+ user_id: 'user_123', // Set after identify()
81
+
82
+ // Timestamps
83
+ timestamp: '2024-01-15T10:30:00Z',
84
+ }
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Configuration
90
+
91
+ ```javascript
92
+ const datalyr = new Datalyr({
93
+ // Required
94
+ apiKey: string,
95
+
96
+ // Optional
97
+ host?: string, // Custom endpoint (default: https://ingest.datalyr.com)
98
+ flushAt?: number, // Batch size (default: 20)
99
+ flushInterval?: number, // Send interval ms (default: 10000)
100
+ timeout?: number, // Request timeout ms (default: 10000)
101
+ retryLimit?: number, // Max retries (default: 3)
102
+ maxQueueSize?: number, // Max queued events (default: 1000)
103
+ debug?: boolean, // Console logging (default: false)
104
+ });
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Event Tracking
110
+
111
+ ### Custom Events
112
+
113
+ Track any action in your application:
24
114
 
25
- // Track an event
115
+ ```javascript
116
+ // Simple event
117
+ await datalyr.track('user_123', 'signup_started');
118
+
119
+ // Event with properties
120
+ await datalyr.track('user_123', 'product_viewed', {
121
+ product_id: 'SKU123',
122
+ product_name: 'Blue Shirt',
123
+ price: 29.99,
124
+ currency: 'USD',
125
+ });
126
+
127
+ // Purchase event
26
128
  await datalyr.track('user_123', 'Purchase Completed', {
27
- amount: 99.99,
129
+ order_id: 'ORD-456',
130
+ total: 99.99,
28
131
  currency: 'USD',
29
- products: ['item_1', 'item_2']
132
+ items: ['SKU123', 'SKU456'],
30
133
  });
134
+ ```
135
+
136
+ ### Page Views
137
+
138
+ Track server-rendered page views:
139
+
140
+ ```javascript
141
+ await datalyr.page('user_123', 'Homepage', {
142
+ url: 'https://example.com',
143
+ referrer: 'https://google.com',
144
+ });
145
+
146
+ await datalyr.page('user_123', 'Product Details', {
147
+ url: 'https://example.com/products/123',
148
+ product_id: 'SKU123',
149
+ });
150
+ ```
31
151
 
32
- // Identify a user
152
+ ---
153
+
154
+ ## User Identity
155
+
156
+ ### Anonymous ID
157
+
158
+ The SDK generates a persistent anonymous ID:
159
+
160
+ ```javascript
161
+ const anonymousId = datalyr.getAnonymousId();
162
+ // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
163
+ ```
164
+
165
+ For attribution preservation, pass the anonymous ID from your browser/mobile SDK instead.
166
+
167
+ ### Identifying Users
168
+
169
+ Link events to a known user:
170
+
171
+ ```javascript
33
172
  await datalyr.identify('user_123', {
34
173
  email: 'user@example.com',
35
174
  name: 'John Doe',
36
- plan: 'premium'
175
+ plan: 'premium',
37
176
  });
177
+ ```
38
178
 
39
- // Track a pageview
40
- await datalyr.page('user_123', 'Homepage', {
41
- url: 'https://example.com',
42
- referrer: 'https://google.com'
43
- });
179
+ ### Groups
44
180
 
45
- // Group a user
181
+ Associate users with companies or teams:
182
+
183
+ ```javascript
46
184
  await datalyr.group('user_123', 'company_456', {
47
185
  name: 'Acme Corp',
48
- industry: 'Technology'
186
+ industry: 'Technology',
187
+ employees: 50,
49
188
  });
50
-
51
- // Clean up when done
52
- await datalyr.close();
53
189
  ```
54
190
 
55
- ## Identity Resolution (New in v1.1.0)
191
+ ---
56
192
 
57
- The SDK now supports anonymous IDs for complete user journey tracking:
193
+ ## Attribution Preservation
194
+
195
+ Pass the anonymous ID from browser/mobile SDKs to preserve attribution data:
58
196
 
59
197
  ```javascript
60
- // Option 1: Pass anonymous_id from browser/mobile for attribution preservation
198
+ // Object signature with anonymousId
61
199
  await datalyr.track({
62
200
  event: 'Purchase Completed',
63
201
  userId: 'user_123',
64
- anonymousId: req.body.anonymous_id, // From browser/mobile SDK
202
+ anonymousId: req.body.anonymous_id, // From browser SDK
65
203
  properties: {
66
204
  amount: 99.99,
67
- currency: 'USD'
68
- }
205
+ currency: 'USD',
206
+ },
69
207
  });
208
+ ```
70
209
 
71
- // Option 2: Use legacy signature (SDK generates anonymous_id)
72
- await datalyr.track('user_123', 'Purchase Completed', {
73
- amount: 99.99
210
+ This links server-side events to the user's browser session, preserving:
211
+ - UTM parameters (utm_source, utm_medium, utm_campaign)
212
+ - Click IDs (fbclid, gclid, ttclid)
213
+ - Referrer and landing page
214
+ - Customer journey touchpoints
215
+
216
+ ---
217
+
218
+ ## Event Queue
219
+
220
+ Events are batched for efficiency.
221
+
222
+ ### Configuration
223
+
224
+ ```javascript
225
+ const datalyr = new Datalyr({
226
+ apiKey: 'dk_your_api_key',
227
+ flushAt: 20, // Send when 20 events queued
228
+ flushInterval: 10000, // Or every 10 seconds
74
229
  });
230
+ ```
75
231
 
76
- // Get the SDK's anonymous ID (useful for server-only tracking)
77
- const anonymousId = datalyr.getAnonymousId();
232
+ ### Manual Flush
233
+
234
+ Send all queued events immediately:
235
+
236
+ ```javascript
237
+ await datalyr.flush();
78
238
  ```
79
239
 
80
- ### Express.js Example with Browser Attribution
240
+ ### Graceful Shutdown
241
+
242
+ Always close the client on application shutdown:
243
+
244
+ ```javascript
245
+ process.on('SIGTERM', async () => {
246
+ await datalyr.close(); // Flushes remaining events
247
+ process.exit(0);
248
+ });
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Framework Examples
254
+
255
+ ### Express.js
81
256
 
82
257
  ```javascript
258
+ import express from 'express';
259
+ import { Datalyr } from '@datalyr/api';
260
+
261
+ const app = express();
262
+ const datalyr = new Datalyr('dk_your_api_key');
263
+
83
264
  app.post('/api/purchase', async (req, res) => {
84
- const { items, anonymous_id } = req.body; // anonymous_id from browser
85
-
86
- // Track with anonymous_id to preserve attribution (fbclid, gclid, etc.)
265
+ const { items, anonymous_id } = req.body;
266
+
267
+ // Track with anonymous_id to preserve attribution
87
268
  await datalyr.track({
88
269
  event: 'Purchase Completed',
89
270
  userId: req.user?.id,
90
- anonymousId: anonymous_id, // Links to browser events!
271
+ anonymousId: anonymous_id,
91
272
  properties: {
92
273
  total: calculateTotal(items),
93
- items: items.length
94
- }
274
+ item_count: items.length,
275
+ },
95
276
  });
96
-
277
+
97
278
  res.json({ success: true });
98
279
  });
99
- ```
100
-
101
- ### Key Benefits:
102
- - **Attribution Preservation**: Never lose fbclid, gclid, ttclid, or lyr tracking
103
- - **Complete Journey**: Track users from web → server → mobile
104
- - **Flexible API**: Support both legacy and new tracking methods
105
280
 
106
- ## Configuration
107
-
108
- ```javascript
109
- const datalyr = new Datalyr({
110
- apiKey: 'your_api_key_here',
111
- host: 'https://api.datalyr.com', // Optional: custom host
112
- flushAt: 20, // Optional: batch size (default: 20)
113
- flushInterval: 10000, // Optional: batch interval in ms (default: 10000)
114
- debug: true, // Optional: enable debug logging (default: false)
115
- timeout: 10000, // Optional: request timeout in ms (default: 10000)
116
- retryLimit: 3, // Optional: max retries (default: 3)
117
- maxQueueSize: 1000 // Optional: max events in queue (default: 1000)
281
+ // Graceful shutdown
282
+ process.on('SIGTERM', async () => {
283
+ await datalyr.close();
284
+ process.exit(0);
118
285
  });
119
286
  ```
120
287
 
121
- ## Stripe Webhook Example
288
+ ### Stripe Webhooks
122
289
 
123
290
  ```javascript
124
- const { Datalyr } = require('@datalyr/api');
125
- const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
291
+ import { Datalyr } from '@datalyr/api';
292
+ import Stripe from 'stripe';
126
293
 
127
- const datalyr = new Datalyr(process.env.DATALYR_API_KEY);
294
+ const datalyr = new Datalyr('dk_your_api_key');
295
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
128
296
 
129
297
  app.post('/webhooks/stripe', async (req, res) => {
130
298
  const sig = req.headers['stripe-signature'];
131
299
  const event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
132
-
300
+
133
301
  switch (event.type) {
134
302
  case 'checkout.session.completed':
303
+ const session = event.data.object;
135
304
  await datalyr.track(
136
- event.data.object.client_reference_id,
305
+ session.client_reference_id,
137
306
  'Purchase Completed',
138
307
  {
139
- amount: event.data.object.amount_total / 100,
140
- currency: event.data.object.currency,
141
- stripeSessionId: event.data.object.id
308
+ amount: session.amount_total / 100,
309
+ currency: session.currency,
310
+ stripe_session_id: session.id,
142
311
  }
143
312
  );
144
313
  break;
145
-
314
+
146
315
  case 'customer.subscription.created':
316
+ const subscription = event.data.object;
147
317
  await datalyr.track(
148
- event.data.object.metadata.userId,
318
+ subscription.metadata.userId,
149
319
  'Subscription Started',
150
320
  {
151
- plan: event.data.object.items.data[0].price.nickname,
152
- mrr: event.data.object.items.data[0].price.unit_amount / 100,
153
- interval: event.data.object.items.data[0].price.recurring.interval
321
+ plan: subscription.items.data[0].price.nickname,
322
+ mrr: subscription.items.data[0].price.unit_amount / 100,
323
+ interval: subscription.items.data[0].price.recurring.interval,
154
324
  }
155
325
  );
156
326
  break;
157
327
  }
158
-
328
+
159
329
  res.json({ received: true });
160
330
  });
161
331
  ```
162
332
 
163
- ## API Reference
333
+ ---
164
334
 
165
- ### `new Datalyr(config)`
335
+ ## TypeScript
166
336
 
167
- Creates a new Datalyr instance.
337
+ ```typescript
338
+ import { Datalyr, TrackOptions, IdentifyOptions } from '@datalyr/api';
168
339
 
169
- ### `track(userId, event, properties?)`
340
+ const datalyr = new Datalyr('dk_your_api_key');
170
341
 
171
- Track a custom event.
342
+ // Type-safe tracking
343
+ const trackOptions: TrackOptions = {
344
+ event: 'Purchase Completed',
345
+ userId: 'user_123',
346
+ anonymousId: 'anon_456',
347
+ properties: {
348
+ amount: 99.99,
349
+ currency: 'USD',
350
+ },
351
+ };
172
352
 
173
- ### `identify(userId, traits?)`
353
+ await datalyr.track(trackOptions);
354
+ ```
174
355
 
175
- Identify a user with traits.
356
+ ---
176
357
 
177
- ### `page(userId, name?, properties?)`
358
+ ## Troubleshooting
178
359
 
179
- Track a pageview.
360
+ ### Events not appearing
180
361
 
181
- ### `group(userId, groupId, traits?)`
362
+ 1. Check API key starts with `dk_`
363
+ 2. Enable `debug: true`
364
+ 3. Call `flush()` to force send
365
+ 4. Check server logs for errors
182
366
 
183
- Associate a user with a group.
367
+ ### Request timeouts
184
368
 
185
- ### `flush()`
369
+ ```javascript
370
+ const datalyr = new Datalyr({
371
+ apiKey: 'dk_your_api_key',
372
+ timeout: 30000, // Increase timeout
373
+ retryLimit: 5, // More retries
374
+ });
375
+ ```
186
376
 
187
- Manually flush the event queue.
377
+ ### Queue full
188
378
 
189
- ### `close()`
379
+ ```javascript
380
+ const datalyr = new Datalyr({
381
+ apiKey: 'dk_your_api_key',
382
+ maxQueueSize: 5000, // Increase queue size
383
+ flushAt: 50, // Larger batches
384
+ });
385
+ ```
190
386
 
191
- Flush remaining events and clean up resources.
387
+ ---
192
388
 
193
389
  ## License
194
390
 
195
- MIT
391
+ MIT
package/dist/index.js CHANGED
@@ -100,7 +100,7 @@ var Datalyr = class {
100
100
  properties: enrichedProperties,
101
101
  context: {
102
102
  library: "@datalyr/api",
103
- version: "1.0.4",
103
+ version: "1.1.0",
104
104
  source: "api"
105
105
  // Explicitly set source for server-side API
106
106
  },
package/dist/index.mjs CHANGED
@@ -75,7 +75,7 @@ var Datalyr = class {
75
75
  properties: enrichedProperties,
76
76
  context: {
77
77
  library: "@datalyr/api",
78
- version: "1.0.4",
78
+ version: "1.1.0",
79
79
  source: "api"
80
80
  // Explicitly set source for server-side API
81
81
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalyr/api",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Datalyr API SDK for server-side tracking",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",