npm package: @ortha/server-utils
Description: Shared server utilities — error response building, cursor-based pagination, SQL helpers, and a generic filter service for Sequelize. Used by all server domain plugins for consistent error handling, pagination, and query filtering.
Latest version: 0.0.4

# @ortha/server-utils

Shared server utilities — error response building, cursor-based pagination, SQL helpers, and a generic filter service for Sequelize. Used by all server domain plugins for consistent error handling, pagination, and query filtering.

## Installation

Internal monorepo dependency — import directly:

```ts
import {
    buildErrorResponse,
    filterService,
    encodeCursor,
    decodeCursor,
    escapeLikeValue
} from '@ortha/server-utils';
```

## Usage

### Error responses

```ts
import { buildErrorResponse, withErrorDetail } from '@ortha/server-utils';

// Build structured error response
const error = buildErrorResponse({
    statusCode: 400,
    code: 'VALIDATION_ERROR',
    message: 'Invalid input'
});

// Add field-level details
withErrorDetail(error, {
    code: 'REQUIRED',
    path: 'email',
    message: 'Email is required'
});
```

### Filter service

```ts
import { filterService } from '@ortha/server-utils';

const service = filterService({
    httpErrors: fastify.httpErrors,
    allowedFields: ['email', 'name', 'isActive'],
    allowedIncludes: {
        UserProject: {
            model: models.UserProject,
            fields: ['projectId']
        }
    }
});

// Parse client-provided filter JSON into Sequelize where clause
const { where, includes } = service.parseAndValidate(request.query.filter);
```

### Pagination

```ts
import { encodeCursor, decodeCursor } from '@ortha/server-utils';

// Encode a cursor from a record's created date + ID
const cursor = encodeCursor({ createdAt, id });

// Decode cursor for the next query
const { createdAt, id } = decodeCursor(cursorString);
```

### SQL helpers

```ts
import { escapeLikeValue, opToSql } from '@ortha/server-utils';

// Escape special characters for LIKE queries
const safe = escapeLikeValue('user%input'); // 'user\\%input'

// Convert Sequelize Op to raw SQL for subqueries
const sql = opToSql(Op.eq); // '='
```

## API Reference

### Error Utilities

| Export                 | Kind     | Description                                 |
| ---------------------- | -------- | ------------------------------------------- |
| `buildErrorResponse()` | function | Create structured error response object     |
| `withErrorDetail()`    | function | Add field-level detail to an error response |

### Pagination

| Export           | Kind     | Description                                       |
| ---------------- | -------- | ------------------------------------------------- |
| `encodeCursor()` | function | Encode record fields into an opaque cursor string |
| `decodeCursor()` | function | Decode cursor string back into record fields      |

### SQL Helpers

| Export              | Kind     | Description                                              |
| ------------------- | -------- | -------------------------------------------------------- |
| `escapeLikeValue()` | function | Escapes `%`, `_`, `\\` for safe SQL LIKE queries         |
| `opToSql()`         | function | Converts Sequelize operator symbols to raw SQL fragments |

### Filter Service

| Export                | Kind    | Description                                                              |
| --------------------- | ------- | ------------------------------------------------------------------------ |
| `filterService()`     | factory | Generic filter parser — validates client JSON filters into Sequelize ops |
| `FilterService`       | type    | Service interface returned by `filterService()`                          |
| `FilterServiceDeps`   | type    | Dependencies: `httpErrors`, `allowedFields`, optional `allowedIncludes`  |
| `ParsedFilter`        | type    | Result of `parseAndValidate` (`where` + `includes`)                      |
| `ParsedInclude`       | type    | Single relation-based `$exists` filter entry                             |
| `IncludeFilterConfig` | type    | Config for an allowed relation in `$exists` filters                      |
| `AllowedIncludes`     | type    | Map of relation names to `IncludeFilterConfig`                           |

## Internal Structure

```
src/lib/
├── errors/index.ts             # Error response builders
├── pagination/index.ts         # Cursor-based pagination helpers
├── escapeLikeValue/index.ts    # SQL LIKE escape utility
├── opToSql/index.ts            # Sequelize Op → raw SQL converter
└── filterService/index.ts      # Generic filter parsing and validation
```

## Key Patterns

- `filterService` is parameterised: callers pass `allowedFields` and optional `allowedIncludes` at construction time.
- `opToSql` is a pure function used for building `EXISTS`/`NOT EXISTS` subqueries.
- `filterService` supports `$exists` filtering on relations with dynamic field conditions.
- Error responses follow a consistent structure with `statusCode`, `code`, `message`, and `details[]`.

## Building

```bash
nx build server-utils
```