# @recomnext/node

**License:** Proprietary (restricted). Not open source. Full terms: [recomnext.com/license](https://recomnext.com/license). Installation constitutes acceptance.

Node.js/TypeScript server SDK for the recomnext recommendation engine.

Documentation: [recomnext.com/resources/sdks](https://recomnext.com/resources/sdks)

## Requirements

- Node.js >= 18 (uses native `fetch`)

## Installation

```bash
npm install @recomnext/node
```

## Quick Start

```typescript
import { RecomnextClient } from '@recomnext/node';

const client = new RecomnextClient({
  baseUrl: 'http://localhost:3000',
  tenantId: 'my-tenant',
});

// Get personalized recommendations
const { items } = await client.recommendations.userToItem('user-123', {
  count: 10,
  scenario: 'home-for-you',
  country: 'IN', // optional ISO-2; omit for global ranking
});

// Ingest items
await client.ingestion.upsertItems([
  { externalId: 'prod-1', category: 'electronics', attributes: { name: 'Laptop', price: 999 } },
]);

// Log interactions
await client.ingestion.logInteractions([
  { userId: 'user-123', itemId: 'prod-1', type: 'view', country: 'IN' },
]);

// Log impressions
await client.ingestion.logImpressions([
  { userId: 'user-123', itemIds: ['prod-1', 'prod-2'], scenarioSlug: 'home-for-you' },
]);
```

## nextQL filters

Recommendation `filter` / `boosters` are **opaque nextQL strings** — the SDK does not parse operators.

```text
'allowed_countries' contains 'IN'
'blocked_regions' not_contains 'EU'
'category' in ['games', 'software']
```

Use `contains` / `not_contains` **only** for array item attributes; use `in` / `not_in` when the filter supplies the list. Missing, null, or non-array attrs fail closed — for denylists every item must have the array (`[]` = not blocked).

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `baseUrl` | string | (required) | Engine URL |
| `tenantId` | string | (required) | Tenant identifier |
| `retries` | number | 3 | Max retry attempts for 5xx errors |
| `timeout` | number | 10000 | Request timeout in ms |
| `backoffFactor` | number | 300 | Base delay for exponential backoff (ms) |

## API Namespaces

- `client.ingestion` — `upsertItems()`, `upsertUsers()`, `logInteractions()`, `logImpressions()`
- `client.recommendations` — `userToItem()`, `itemToItem()`, `recentlyViewed()`, `similarProducts()`, `itemsToItems()`, `search()`, `searchV2()`, `autocomplete()`, `getSearchV2Settings()`
- `client.scenarios` — `list()`, `get()`, `create()`, `update()`, `delete()`
- `client.catalog` — `listItems()`, `getItem()`, `listUsers()`, `getUser()`
- `client.identity` — `merge()`

## Search

```typescript
// Search v1 — hybrid semantic search
const v1 = await client.recommendations.search('wireless headphones', {
  count: 20,
  category: 'electronics',
  userId: 'user-123',
});

// Search v2 — contextual multilingual hybrid retrieval
const v2 = await client.recommendations.searchV2('xbox game', {
  count: 20,
  locale: 'english',
  userId: 'user-123',
  debugScores: true,
});

console.log(v2.query.constraints);
console.log(v2.meta.legsUsed);

// Prefix autocomplete
const { suggestions } = await client.recommendations.autocomplete('xbo', { limit: 8 });
```
