diff --git a/readme.md b/readme.md index d0bd47f1..4d9e0778 100644 --- a/readme.md +++ b/readme.md @@ -25,110 +25,309 @@

-# FingerprintJS Server API Node.js SDK -Node.js wrapper for [FingerprintJS Server API](https://dev.fingerprint.com/docs/server-api) +# Fingerprint Server API Node.js SDK -## Usage +[Fingerprint](https://fingerprint.com) is a device intelligence platform offering 99.5% accurate visitor identification. -Install package using npm -``` sh -npm i @fingerprintjs/fingerprintjs-pro-server-api -``` +The Fingerprint Server Node SDK is an easy way to interact with the Fingerprint [Server API](https://dev.fingerprint.com/reference/pro-server-api) from your Node application. You can retrieve visitor history or individual identification events. -Or install package using yarn -``` sh -yarn add @fingerprintjs/fingerprintjs-pro-server-api -``` +## Requirements + +TypeScript support: + +- TypeScript 4.5.5 or higher + +Supported runtimes: + +- Node.js 18 LTS or higher (we support all [Node LTS releases before end-of-life](https://nodejs.dev/en/about/releases/)). +- Deno and Bun might work but are not actively tested. +- "Edge" runtimes might work with some modifications but are not actively tested.
+ See "edge" runtimes compatibility + + This SDK can be made compatible with JavaScript "edge" runtimes that do not support all Node APIs, for example, [Vercel Edge Runtime](https://edge-runtime.vercel.app/), or [Cloudflare Workers](https://developers.cloudflare.com/workers/). + + To make it work, replace the SDK's built-in `fetch` function (which relies on Node APIs) with the runtime's native `fetch` function. Pass the function into the constructor with proper binding: + + ```js + const client = new FingerprintJsServerApiClient({ + region: Region.EU, + apiKey: apiKey, + fetch: fetch.bind(globalThis), + }); + ``` + +
+ +## How to install + +Install the package using your favorite package manager: + +* NPM: + + ```sh + npm i @fingerprintjs/fingerprintjs-pro-server-api + ``` +* Yarn: + ```sh + yarn add @fingerprintjs/fingerprintjs-pro-server-api + ``` +* pnpm: + ```sh + pnpm i @fingerprintjs/fingerprintjs-pro-server-api + ``` + +## Getting started + +Initialize the client instance and use it to make API requests. You need to specify your Fingerprint [Secret API key](https://dev.fingerprint.com/docs/quick-start-guide#server-api) and the region of your Fingerprint application. -### Usage of the Server API ```ts import { FingerprintJsServerApiClient, Region } from '@fingerprintjs/fingerprintjs-pro-server-api'; -// Init client with the given region and the secret api_key -const client = new FingerprintJsServerApiClient({region: Region.Global, apiKey: ""}); +const client = new FingerprintJsServerApiClient({ + apiKey: '', + region: Region.Global, +}); -// Get visitor history -client.getVisitorHistory("").then(visitorHistory => { - console.log(visitorHistory); +// Get visit history of a specific visitor +client.getVisitorHistory('').then((visitorHistory) => { + console.log(visitorHistory); }); +// Get a specific identification event +client.getEvent('').then((event) => { + console.log(event); +}); ``` -### Usage of the built-in webhook visit type +### Using with TypeScript + +#### Webhook types + +When handling [Webhooks](https://dev.fingerprint.com/docs/webhooks) coming from Fingerprint, you can cast the payload as the built-in `VisitWebhook` type: + ```ts const visit = visitWebhookBody as unknown as VisitWebhook; ``` -### Compatibility -This SDK might be also compatible, however not actively tested, with JavaScript "edge" runtimes that do not support all Node APIs, for example, [Vercel Edge Runtime](https://edge-runtime.vercel.app/), or [Cloudflare Workers](https://developers.cloudflare.com/workers/). +#### Narrowing error types -To make it work, replace the SDK's built-in fetch function (which relies on Node APIs) with the runtime's native `fetch` function. Pass the function into the constructor with proper binding: -```js -const client = new FingerprintJsServerApiClient({ - region: Region.EU, - apiKey: apiKey, - fetch: fetch.bind(globalThis), -}); +The `getEvent` and `getVisitorHistory` methods can throw `EventError` and `VisitorsError`. +You can use the provided `isVisitorsError` and `isEventError` type guards to narrow down error types: + +```typescript +import { isVisitorsError, isEventError } from '@fingerprintjs/fingerprintjs-pro-server-api'; + +client + .getVisitorHistory('', filter) + .then((result) => console.log(result)) + .catch((err) => { + if (isVisitorsError(err)) { + if (err.code === 429) { + // VisitorsError429 type + retryLater(err.retryAfter); // this function needs to be implemented on your side + } else { + console.log('error: ', err.error); + } + } else { + console.log('unknown error: ', err); + } + }); + +client + .getEvent('') + .then((result) => console.log(result)) + .catch((err) => { + if (isEventError(err)) { + console.log(`error ${err.code}: `, err.error.message); + } else { + console.log('unknown error: ', err); + } + }); ``` -## API +## API Reference + +### `constructor({region: Region, apiKey: string})` -#### `FingerprintJsServerApiClient({region: Region, apiKey: string})` constructor Creates an instance of the client. -##### Usage + +#### Usage + ```js -const client = new FingerprintJsServerApiClient({region: Region.EU, apiKey: ""}); +const client = new FingerprintJsServerApiClient({ region: Region.EU, apiKey: '' }); ``` -##### Params + +#### Params + - `region: Region` - a region of the server, possible values: `Region.EU`, `Region.AP`, or `Region.Global` - `apiKey: string` - secret API key from the [FingerprintJS dashboard](https://dashboard.fingerprint.com/) - `fetch?: typeof fetch` - optional implementation of `fetch` function (defaults to `node-fetch`) + +--- + +### `getEvent(requestId: string): Promise` + +Retrieves a specific identification event with the information from each activated product — Identification and all active [Smart signals](https://dev.fingerprint.com/docs/smart-signals-overview). + +#### Usage + +```typescript +client + .getEvent('') + .then((eventInfo) => { + console.log(eventInfo); + }) + .catch((error) => { + if (error.status === 403 || error.status === 404) { + console.log(error.code, error.message); + } + }); +``` + +#### Params + +- `requestId: string` - identifier of the event + +#### Returns + +- `Promise` - promise with event response + +##### `EventResponse` + +For more information, see the [Server API documentation](https://dev.fingerprint.com/reference/getevent). + +```json +{ + "products": { + "identification": { + "data": { + "visitorId": "Ibk1527CUFmcnjLwIs4A9", + "requestId": "0KSh65EnVoB85JBmloQK", + "incognito": true, + "linkedId": "somelinkedId", + "time": "2019-05-21T16:40:13Z", + "timestamp": 1582299576512, + "url": "https://www.example.com/login", + "ip": "61.127.217.15", + "ipLocation": { + "accuracyRadius": 10, + "latitude": 49.982, + "longitude": 36.2566, + "postalCode": "61202", + "timezone": "Europe/Dusseldorf", + "city": { + "name": "Dusseldorf" + }, + "continent": { + "code": "EU", + "name": "Europe" + }, + "country": { + "code": "DE", + "name": "Germany" + }, + "subdivisions": [ + { + "isoCode": "63", + "name": "North Rhine-Westphalia" + } + ] + }, + "browserDetails": { + "browserName": "Chrome", + "browserMajorVersion": "74", + "browserFullVersion": "74.0.3729", + "os": "Windows", + "osVersion": "7", + "device": "Other", + "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ...." + }, + "confidence": { + "score": 0.97 + }, + "visitorFound": true, + "firstSeenAt": { + "global": "2022-03-16T11:26:45.362Z", + "subscription": "2022-03-16T11:31:01.101Z" + }, + "lastSeenAt": { + "global": "2022-03-16T11:28:34.023Z", + "subscription": null + } + } + }, + "botd": { + "data": { + "bot": { + "result": "notDetected" + }, + "url": "https://example.com/login", + "ip": "61.127.217.15", + "time": "2019-05-21T16:40:13Z" + } + } + } +} +``` + --- -#### `client.getVisitorHistory(visitorId: string, filter?: VisitorHistoryFilter): Promise` -Gets history for the given visitor and given filter, returns a promise with visitor history response. -##### Usage +### `getVisitorHistory(visitorId: string, filter?: VisitorHistoryFilter): Promise` + +Retrieves event history for the specific visitor using the given filter, returns a promise with visitor history response. + +#### Usage + ```js -client.getVisitorHistory("", filter) - .then(visitorHistory => { +client + .getVisitorHistory('', filter) + .then((visitorHistory) => { console.log(visitorHistory); }) - .catch(error => { + .catch((error) => { if (error.status === 403) { console.log(error.error); } else if (error.status === 429) { - retryLater(error.retryAfter); // this function needs to be implemented on your side + retryLater(error.retryAfter); // this function needs to be implemented on your side } }); ``` -##### Params + +#### Params + - `visitorId: string` - identifier of the visitor -- `filter?: VisitorHistoryFilter` - visitor history filter, more info in [the API documentation](https://dev.fingerprint.com/docs/server-api#query-parameters) -##### Returns -- `Promise` - promise with visitor history response ---- -#### `VisitorHistoryFilter` -Filter for querying API - see [query parameters](https://dev.fingerprint.com/docs/server-api#query-parameters). -##### Usage +- `filter?: VisitorHistoryFilter` - visitor history filter (details below) + +##### `VisitorHistoryFilter` + +Filter for querying the [visitors Server API endpoint](https://dev.fingerprint.com/reference/getvisits). + +Usage: + ```js const filter = { - request_id: "", - linked_id: "", - limit: 5, - paginationKey: "" + request_id: '', + linked_id: '', + limit: 5, + paginationKey: '', }; ``` -##### Properties + +Properties: + - `request_id: string` - filter visits by `requestId`. Every identification request has a unique identifier associated with it called `requestId`. This identifier is returned to the client in the identification [result](https://dev.fingerprint.com/docs/js-agent#requestid). When you filter visits by `requestId`, only one visit will be returned. + - `linked_id: string` - filter visits by your custom identifier. You can use [`linkedId`](https://dev.fingerprint.com/docs/js-agent#linkedid) to associate identification requests with your own identifier, for example: session ID, purchase ID, or transaction ID. You can then use this `linked_id` parameter to retrieve all events associated with your custom identifier. + - `limit: number` - limit scanned results. For performance reasons, the API first scans some number of events before filtering them. Use `limit` to specify how many events are scanned before they are filtered by `requestId` or `linkedId`. Results are always returned sorted by the timestamp (most recent first). By default, the most recent 100 visits are scanned, the maximum is 500. -- `paginationKey: string` - use `paginationKey` to get the next page of results. + +- `paginationKey: string` - use `paginationKey` to get the next page of results. When more results are available (e.g., you requested 200 results using `limit` parameter, but a total of 600 results are available), the `paginationKey` top-level attribute is added to the response. The key corresponds to the `requestId` of the last returned event. In the following request, use that value in the `paginationKey` parameter to get the next page of results: @@ -136,9 +335,15 @@ const filter = { 2. Use `response.paginationKey` to get the next page of results: `GET api-base-url/visitors/:visitorId?limit=200&paginationKey=1683900801733.Ogvu1j` Pagination happens during scanning and before filtering, so you can get less visits than the `limit` you specified with more available on the next page. When there are no more results available for scanning, the `paginationKey` attribute is not returned. ---- -#### Server `VisitorsResponse` response -Find more info in [the API documentation](https://dev.fingerprint.com/docs/server-api#response) + +#### Returns + +- `Promise` - promise with the visitor history response + +##### `VisitorsResponse` + +For more information, see the [Server API documentation](https://dev.fingerprint.com/reference/getvisits). + ```json { "visitorId": "Ibk1527CUFmcnjLwIs4A9", @@ -183,7 +388,7 @@ Find more info in [the API documentation](https://dev.fingerprint.com/docs/serve "os": "Windows", "osVersion": "7", "device": "Other", - "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ....", + "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ...." }, "confidence": { "score": 0.97 @@ -204,133 +409,10 @@ Find more info in [the API documentation](https://dev.fingerprint.com/docs/serve } ``` -#### `client.getEvent(requestId: string): Promise` -Get events with all the information from each activated product - Bot Detection and Identification. -##### Usage -```typescript -client.getEvent("") - .then(eventInfo => { - console.log(eventInfo); - }) - .catch(error => { - if (error.status === 403 || error.status === 404) { - console.log(error.code, error.message); - } - }); -``` -##### Params -- `requestId: string` - identifier of the event -##### Returns -- `Promise` - promise with event response ---- -#### Server `EventResponse` response -Find more info in the [API documentation](https://dev.fingerprint.com/docs/server-api#response-1). -```json -{ - "products": { - "identification": { - "data": { - "visitorId": "Ibk1527CUFmcnjLwIs4A9", - "requestId": "0KSh65EnVoB85JBmloQK", - "incognito": true, - "linkedId": "somelinkedId", - "time": "2019-05-21T16:40:13Z", - "timestamp": 1582299576512, - "url": "https://www.example.com/login", - "ip": "61.127.217.15", - "ipLocation": { - "accuracyRadius": 10, - "latitude": 49.982, - "longitude": 36.2566, - "postalCode": "61202", - "timezone": "Europe/Dusseldorf", - "city": { - "name": "Dusseldorf" - }, - "continent": { - "code": "EU", - "name": "Europe" - }, - "country": { - "code": "DE", - "name": "Germany" - }, - "subdivisions": [ - { - "isoCode": "63", - "name": "North Rhine-Westphalia" - } - ], - }, - "browserDetails": { - "browserName": "Chrome", - "browserMajorVersion": "74", - "browserFullVersion": "74.0.3729", - "os": "Windows", - "osVersion": "7", - "device": "Other", - "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) ....", - }, - "confidence": { - "score": 0.97 - }, - "visitorFound": true, - "firstSeenAt": { - "global": "2022-03-16T11:26:45.362Z", - "subscription": "2022-03-16T11:31:01.101Z" - }, - "lastSeenAt": { - "global": "2022-03-16T11:28:34.023Z", - "subscription": null - } - } - }, - "botd": { - "data": { - "bot": { - "result": "notDetected" - }, - "url": "https://example.com/login", - "ip": "61.127.217.15", - "time": "2019-05-21T16:40:13Z" - } - } - } -} -``` -### Using with TypeScript - -`getEvent` and `getVisitorHistory` methods return generic types `Promise` and `Promise` and throw `EventError` and `VisitorsError`. +## Support and feedback -You can use typeguards to narrow error types as in example below. +To report problems, ask questions or provide feedback, please use [Issues](https://github.com/fingerprintjs/fingerprintjs-pro-server-api-node-sdk/issues). If you need private support, you can email us at [oss-support@fingerprint.com](mailto:oss-support@fingerprint.com). -```typescript -import { isVisitorsError, isEventError } from '@fingerprintjs/fingerprintjs-pro-server-api'; +## License -client - .getVisitorHistory("", filter) - .then(result => console.log(result)) - .catch(err => { - if (isVisitorsError(err)) { - if (err.code === 429) { - // VisitorsError429 type - retryLater(err.retryAfter); // this function needs to be implemented on your side - } else { - console.log('error: ', err.error) - } - } else { - console.log('unknown error: ', err) - } - }); - -client - .getEvent("") - .then(result => console.log(result)) - .catch(err => { - if (isEventError(err)) { - console.log(`error ${err.code}: `, err.error.message) - } else { - console.log('unknown error: ', err) - } - }); -``` +This project is licensed under the [MIT license](./LICENSE).