Skip to content

Commit d4b481c

Browse files
authored
Merge pull request #7368 from segmentio/update-serverless-info
Update node docs
2 parents fcd1ecb + 8f2c5bd commit d4b481c

File tree

2 files changed

+132
-87
lines changed

2 files changed

+132
-87
lines changed

src/connections/sources/catalog/libraries/server/node/index.md

+130-85
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ All of Segment's server-side libraries are built for high-performance, so you ca
1515
## Getting Started
1616

1717
> warning ""
18-
> Make sure you're using a version of Node that's 16 or higher.
18+
> Make sure you're using a version of Node that's 18 or higher.
1919
2020
1. Run the relevant command to add Segment's Node library module to your `package.json`.
2121

@@ -289,25 +289,105 @@ Setting | Details
289289
290290
See the complete `AnalyticsSettings` interface [in the analytics-next repository](https://github.com/segmentio/analytics-next/blob/master/packages/node/src/app/settings.ts){:target="_blank"}.
291291
292-
## Usage in serverless environments
292+
## Usage in serverless environments and non-node runtimes
293+
Segment supports a variety of runtimes, including, but not limited to:
294+
- AWS Lambda
295+
- Cloudflare Workers
296+
- Vercel Edge Functions
297+
- Web Workers / Browser (no device mode destination support)
293298
294-
When calling Track within functions in serverless runtime environments, wrap the call in a `Promise` and `await` it to avoid having the runtime exit or freeze:
299+
### Usage in AWS Lambda
300+
- [AWS lambda execution environment](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html){:target="_blank"} is challenging for typically non-response-blocking async activities like tracking or logging, since the runtime terminates or freezes after a response is emitted.
295301
296-
```js
297-
await new Promise((resolve) =>
298-
analytics().track({ ... }, resolve)
299-
)
302+
Here is an example of using analytics.js within a handler:
303+
```ts
304+
const { Analytics } = require('@segment/analytics-node');
305+
306+
// Preferable to create a new analytics instance per-invocation. Otherwise, we may get a warning about overlapping flush calls. Also, custom plugins have the potential to be stateful, so we prevent those kind of race conditions.
307+
const createAnalytics = () => new Analytics({
308+
writeKey: '<MY_WRITE_KEY>',
309+
}).on('error', console.error);
310+
311+
module.exports.handler = async (event) => {
312+
const analytics = createAnalytics()
313+
314+
analytics.identify({ ... })
315+
analytics.track({ ... })
316+
317+
// ensure analytics events get sent before program exits
318+
await analytics.flush()
319+
320+
return {
321+
statusCode: 200,
322+
};
323+
....
324+
};
325+
```
326+
327+
### Usage in Vercel Edge Functions
328+
329+
```ts
330+
import { Analytics } from '@segment/analytics-node';
331+
import { NextRequest, NextResponse } from 'next/server';
332+
333+
const createAnalytics = () => new Analytics({
334+
writeKey: '<MY_WRITE_KEY>',
335+
}).on('error', console.error)
336+
337+
export const config = {
338+
runtime: 'edge',
339+
};
340+
341+
export default async (req: NextRequest) => {
342+
const analytics = createAnalytics()
343+
344+
analytics.identify({ ... })
345+
analytics.track({ ... })
346+
347+
// ensure analytics events get sent before program exits
348+
await analytics.flush()
349+
350+
return NextResponse.json({ ... })
351+
};
300352
```
301353
302-
See the complete documentation on [Usage in AWS Lambda](https://github.com/segmentio/analytics-next/blob/master/packages/node/README.md#usage-in-aws-lambda){:target="_blank"}, [Usage in Vercel Edge Functions](https://github.com/segmentio/analytics-next/blob/master/packages/node/README.md#usage-in-vercel-edge-functions){:target="_blank"}, and [Usage in Cloudflare Workers](https://github.com/segmentio/analytics-next/blob/master/packages/node/README.md#usage-in-cloudflare-workers){:target="_blank"}
354+
### Usage in Cloudflare Workers
355+
356+
```ts
357+
import { Analytics, Context } from '@segment/analytics-node';
358+
359+
360+
const createAnalytics = () => new Analytics({
361+
writeKey: '<MY_WRITE_KEY>',
362+
}).on('error', console.error);
363+
364+
export default {
365+
async fetch(
366+
request: Request,
367+
env: Env,
368+
ctx: ExecutionContext
369+
): Promise<Response> {
370+
const analytics = createAnalytics()
371+
372+
analytics.identify({ ... })
373+
analytics.track({ ... })
374+
375+
// ensure analytics events get sent before program exits
376+
await analytics.flush()
377+
378+
return new Response(...)
379+
},
380+
};
381+
382+
```
303383
304384
## Graceful shutdown
305-
Avoid losing events after shutting down your console. Call `.closeAndFlush()` to stop collecting new events and flush all existing events. If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved.
385+
Avoid losing events after shutting down your console. Call `.flush({ close: true })` to stop collecting new events and flush all existing events. If a callback on an event call is included, this also waits for all callbacks to be called, and any of their subsequent promises to be resolved.
306386
307387
```javascript
308-
await analytics.closeAndFlush()
388+
await analytics.flush({ close: true })
309389
// or
310-
await analytics.closeAndFlush({ timeout: 5000 }) // force resolve after 5000ms
390+
await analytics.flush({ close: true, timeout: 5000 }) // force resolve after 5000ms
311391
```
312392
313393
Here's an example of how to use graceful shutdown:
@@ -316,7 +396,7 @@ const app = express()
316396
const server = app.listen(3000)
317397
318398
const onExit = async () => {
319-
await analytics.closeAndFlush()
399+
await analytics.flush({ close: true })
320400
server.close(() => {
321401
console.log("Gracefully closing server...")
322402
process.exit()
@@ -326,15 +406,15 @@ const onExit = async () => {
326406
```
327407
328408
### Collect unflushed events
329-
If you need to preserve all of your events in the instance of a forced timeout, even ones that came in after analytics.closeAndFlush() was called, you can still collect those events by using:
409+
If you need to preserve all of your events in the instance of a forced timeout, even ones that came in after analytics.flush({ close: true }) was called, you can still collect those events by using:
330410
331411
```javascript
332412
const unflushedEvents = []
333413
334414
analytics.on('call_after_close', (event) => unflushedEvents.push(events))
335-
await analytics.closeAndFlush()
415+
await analytics.flush({ close: true })
336416
337-
console.log(unflushedEvents) // all events that came in after closeAndFlush was called
417+
console.log(unflushedEvents) // all events that came in after flush was called
338418
```
339419
340420
## Regional configuration
@@ -362,22 +442,17 @@ analytics.on('error', (err) => console.error(err))
362442
363443
364444
### Event emitter interface
365-
The event emitter interface allows you to track events, like Track and Identify calls, and it calls the function you provided with some arguments upon successful delivery. `error` emits on delivery error.
366-
367-
```javascript
368-
analytics.on('error', (err) => console.error(err))
445+
The event emitter interface allows you to pass a callback which will be invoked whenever a specific emitter event occurs in your app, such as when a certain method call is made.
369446
370-
analytics.on('identify', (ctx) => console.log(ctx))
447+
For example:
371448
449+
```javascript
372450
analytics.on('track', (ctx) => console.log(ctx))
373-
```
374-
375-
Use the emitter to log all HTTP Requests.
451+
analytics.on('error', (err) => console.error(err))
376452
377-
```javascript
378-
analytics.on('http_request', (event) => console.log(event))
379453
380-
// when triggered, emits an event of the shape:
454+
// when triggered, emits an event of the shape:
455+
analytics.on('http_request', (event) => console.log(event))
381456
{
382457
url: 'https://api.segment.io/v1/batch',
383458
method: 'POST',
@@ -388,32 +463,43 @@ Use the emitter to log all HTTP Requests.
388463
body: '...',
389464
}
390465
```
466+
467+
### Emitter Types
391468
469+
The following table documents all the emitter types available in the Analytics Node.js library:
392470
393-
## Plugin architecture
394-
When you develop in [Analytics.js 2.0](/docs/connections/sources/catalog/libraries/website/javascript/), the plugins you write can improve functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done.
471+
| Emitter Type | Description |
472+
|-------------------|-----------------------------------------------------------------------------|
473+
| `error` | Emitted when there is an error after SDK initialization. |
474+
| `identify` | Emitted when an Identify call is made.
475+
| `track` | Emitted when a Track call is made.
476+
| `page` | Emitted when a Page call is made.
477+
| `group` | Emitted when a Group call is made.
478+
| `alias` | Emitted when an Alias call is made.
479+
| `flush` | Emitted after a batch is flushed.
480+
| `http_request` | Emitted when an HTTP request is made. |
481+
| `register` | Emitted when a plugin is registered
482+
| `call_after_close`| Emitted when an event is received after the flush with `{ close: true }`. |
395483
396-
Though middlewares function the same as plugins, it's best to use plugins as they are easier to implement and are more testable.
484+
These emitters allow you to hook into various stages of the event lifecycle and handle them accordingly.
397485
398-
### Plugin categories
399-
Plugins are bound by Analytics.js 2.0 which handles operations such as observability, retries, and error handling. There are two different categories of plugins:
400-
* **Critical Plugins**: Analytics.js expects this plugin to be loaded before starting event delivery. Failure to load a critical plugin halts event delivery. Use this category sparingly, and only for plugins that are critical to your tracking.
401-
* **Non-critical Plugins**: Analytics.js can start event delivery before this plugin finishes loading. This means your plugin can fail to load independently from all other plugins. For example, every Analytics.js destination is a non-critical plugin. This makes it possible for Analytics.js to continue working if a partner destination fails to load, or if users have ad blockers turned on that are targeting specific destinations.
402486
403-
> info ""
404-
> Non-critical plugins are only non-critical from a loading standpoint. For example, if the `before` plugin crashes, this can still halt the event delivery pipeline.
487+
## Plugin architecture
488+
The plugins you write can improve functionality, enrich data, and control the flow and delivery of events. From modifying event payloads to changing analytics functionality, plugins help to speed up the process of getting things done.
489+
405490
406-
Non-critical plugins run through a timeline that executes in order of insertion based on the entry type. Segment has these five entry types of non-critical plugins:
491+
### Plugin categories
492+
Segment has these five entry types of plugins:
407493
408-
| Type | Details
409-
------ | --------
410-
| `before` | Executes before event processing begins. These are plugins that run before any other plugins run. <br><br>For example, validating events before passing them along to other plugins. A failure here could halt the event pipeline.
411-
| `enrichment` | Executes as the first level of event processing. These plugins modify an event.
412-
| `destination` | Executes as events begin to pass off to destinations. <br><br> This doesn't modify the event outside of the specific destination, and failure doesn't halt the execution.
413-
| `after` | Executes after all event processing completes. You can use this to perform cleanup operations. <br><br>An example of this is the [Segment.io Plugin](https://github.com/segmentio/analytics-next/blob/master/packages/browser/src/plugins/segmentio/index.ts){:target="_blank"} which waits for destinations to succeed or fail so it can send it observability metrics.
414-
| `utility` | Executes once during the bootstrap, to give you an outlet to make any modifications as to how Analytics.js works internally. This allows you to augment Analytics.js functionality.
494+
| Type | Details
495+
| ------------- | ------------- |
496+
| `before` | Executes before event processing begins. These are plugins that run before any other plugins run. Thrown errors here can block the event pipeline. Source middleware added using `addSourceMiddleware` is treated as a `before` plugin. No events send to destinations until `.load()` method is resolved. |
497+
| `enrichment` | Executes as the first level of event processing. These plugins modify an event. Thrown errors here can block the event pipeline. No events send to destinations until `.load()` method is resolved. |
498+
| `destination` | Executes as events begin to pass off to destinations. Segment.io is implemented as a destination plugin. Thrown errors here will _not_ block the event pipeline. |
499+
| `after` | Executes after all event processing completes. You can use this to perform cleanup operations. |
500+
| `utility` | Executes _only once_ during the bootstrap. Gives you access to the analytics instance using the plugin's `load()` method. This doesn't allow you to modify events. |
415501
416-
### Example plugins
502+
### Example plugin
417503
Here's an example of a plugin that converts all track event names to lowercase before the event goes through the rest of the pipeline:
418504
419505
```js
@@ -430,49 +516,8 @@ export const lowercase: Plugin = {
430516
return ctx
431517
}
432518
}
433-
434-
const identityStitching = () => {
435-
let user
436-
437-
const identity = {
438-
// Identifies your plugin in the Plugins stack.
439-
// Access `window.analytics.queue.plugins` to see the full list of plugins
440-
name: 'Identity Stitching',
441-
// Defines where in the event timeline a plugin should run
442-
type: 'enrichment',
443-
version: '0.1.0',
444-
445-
// Used to signal that a plugin has been property loaded
446-
isLoaded: () => user !== undefined,
447-
448-
// Applies the plugin code to every `identify` call in Analytics.js
449-
// You can override any of the existing types in the Segment Spec.
450-
async identify(ctx) {
451-
// Request some extra info to enrich your `identify` events from
452-
// an external API.
453-
const req = await fetch(
454-
`https://jsonplaceholder.typicode.com/users/${ctx.event.userId}`
455-
)
456-
const userReq = await req.json()
457-
458-
// ctx.updateEvent can be used to update deeply nested properties
459-
// in your events. It's a safe way to change events as it'll
460-
// create any missing objects and properties you may require.
461-
ctx.updateEvent('traits.custom', userReq)
462-
user.traits(userReq)
463-
464-
// Every plugin must return a `ctx` object, so that the event
465-
// timeline can continue processing.
466-
return ctx
467-
},
468-
}
469-
470-
return identity
471-
}
472519
```
473520
474-
You can view Segment's [existing plugins](https://github.com/segmentio/analytics-next/tree/master/packages/browser/src/plugins){:target="_blank"} to see more examples.
475-
476521
### Register a plugin
477522
Registering plugins enable you to modify your analytics implementation to best fit your needs. You can register a plugin using this:
478523

src/connections/sources/catalog/libraries/server/node/migration.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ If you're using the [classic version of Analytics Node.js](/docs/connections/sou
3232

3333
<br> Before:
3434
```javascript
35-
await analytics.flush(function(err, batch) {
35+
await analytics.flush((err, batch) => {
3636
console.log('Flushed, and now this program can exit!');
3737
});
3838
```
3939

4040
After:
4141
```javascript
42-
await analytics.closeAndFlush()
42+
await analytics.flush({ close: true })
4343
```
4444

4545
### Key differences between the classic and updated version

0 commit comments

Comments
 (0)