# CLAUDE.md — graphql-knowledge-search

This file provides guidance for AI assistants working in the graphql-knowledge-search repository.

## Project Overview

**graphql-knowledge-search** is an Apollo GraphQL server (TypeScript, Node.js) that powers search functionality for the Orchard Insights platform. It queries OpenSearch (AWS managed) for full-text and semantic search across the music catalog — artists, labels, sound recordings, products, and playlists. Part of the Apollo Federation supergraph served via `graphql-router`.

## Essential Commands

```bash
# Development
yarn start                  # Dev server with hot-reload (ts-node + nodemon)

# Build
yarn build                  # Production build (tsc + tscpaths + copy schema)

# Testing
yarn test                   # Full suite: format check + lint + unit tests
yarn test:unit              # Jest unit tests only
yarn test:unit:watch        # Watch mode
yarn test:integration       # Integration tests (requires running server + BASE_URL)
yarn test:types             # TypeScript type checking (no emit)

# Linting & Formatting
yarn lint                   # ESLint + GraphQL schema linting
yarn lint:fix               # Auto-fix lint issues
yarn format                 # Prettier formatting
yarn format:check           # Check formatting only

# Code Generation
yarn generate:types         # Generate TypeScript types from GraphQL schema
```

To run a single test file:
```bash
yarn test:unit -- --testPathPattern="path/to/test"
```

## Setup

Copy `.env.shadow` to `.env` before running locally: `cp .env.shadow .env`

## Architecture

### Tech Stack

| Layer | Technology |
|-------|-----------|
| Framework | Apollo Server 5.0.0 (with @theorchard/graphql-server 4.1.0 wrapper) |
| Language | TypeScript 5.9, Node.js >=22.18.0 |
| GraphQL | Schema-first, Apollo Federation |
| Search | OpenSearch 3.5 (@opensearch-project/opensearch) |
| Caching | Redis via Keyv + ErrorsAreMissesCache (optional, in-memory default) |
| Validation | Zod 4.1 |
| Testing | Jest 29.7 + ts-jest |
| Linting | ESLint 9 (@theorchard/eslint-config-ts-prettier) |
| Formatting | Prettier 3.2 |
| Observability | Sentry 9.43, Datadog (dd-trace) |
| Feature Flags | Split.io |

### Directory Structure

```
src/
├── connectors/              # DataSource abstraction layer
│   ├── elasticSearch/       # OpenSearch queries + DSL builders
│   ├── ows-account/         # Account service connector
│   ├── ows-features/        # Feature flags connector
│   ├── ows-pdp/             # PDP service connector
│   ├── ows-permissions/     # Permissions connector
│   └── __tests__/
├── plugins/                 # Apollo Server plugins
│   └── queryResponseCachePlugin  # Custom response cache
├── resolvers/               # GraphQL resolvers
│   ├── types/               # Mapper Key interfaces
│   ├── utils/               # Resolver utilities
│   └── __tests__/
├── schema/                  # Modular .graphql files
│   └── _federation.graphql  # Federation directives
├── constants/               # Application constants
├── generated/               # Auto-generated types (DO NOT EDIT)
├── __mocks__/               # Jest mocks
├── config.ts                # Environment configuration
├── server.ts                # Server entry point
└── types.ts                 # Shared TypeScript types

tests/
├── integration/             # Integration tests
│   └── specs/
└── generated/               # Test type definitions
```

### Data Flow

```
GraphQL Query → Resolver → DataLoader (Zod-validated) → Connector → OpenSearch / OWS API
                                     ↑
                              Redis / in-memory cache
```

## Key Patterns

### OpenSearch DSL Builder

The `src/connectors/elasticSearch/` directory contains a custom OpenSearch query DSL builder. When modifying search logic, work within this abstraction rather than writing raw OpenSearch queries.

### Custom Response Cache Plugin

`src/plugins/queryResponseCachePlugin` implements a custom Apollo response cache. It uses `ErrorsAreMissesCache` — Redis errors are treated as cache misses (graceful degradation).

### Zod Validation

All external API responses are validated with Zod schemas. Schemas use `.transform()` to map API snake_case to camelCase. Always add/update Zod schemas when changing connector fetches.

### DataLoader Factory Pattern

Each DataLoader is a module exporting:
- `create<Name>DataLoader(post)` factory function
- `<Name>DataLoader` type alias
- `dataSchema` (Zod schema)
- `cacheKeyFn` (format: `TypeName:${key}`)

### Mapper Keys

Every GraphQL object type has a `*Key` interface in `src/resolvers/types/`. These define the parent value that resolvers receive. Register new types in the codegen config.

### Federation

This service participates in Apollo Federation. The `_federation.graphql` file defines entity references. Changes to federated types must be coordinated with `graphql-router` supergraph composition.

## Environment Variables

| Variable | Purpose |
|----------|---------|
| `ENV` | `qa` / `prod` |
| `NODE_ENV` | `development` / `test` / `production` |
| `PORT` | Server port (default 8085) |
| `OPENSEARCH_URL` | OpenSearch cluster endpoint |
| `OPENSEARCH_USERNAME` | OpenSearch auth username |
| `OPENSEARCH_PASSWORD` | OpenSearch auth password |
| `CACHE_USE_REDIS` | Enable Redis caching (`true`/`false`) |
| `CACHE_REDIS_HOST` | Redis host |
| `SPLIT_API_KEY` | Split.io feature flag API key |
| `OUTPUT_QUERY_DSL` | Debug: log OpenSearch query DSL |
| `OUTPUT_PERMISSIONS` | Debug: log permission checks |

## Testing

- **Unit tests**: `src/**/__tests__/*` — mock OpenSearch and OWS connectors
- **Integration tests**: `tests/integration/` — require `BASE_URL` env pointing to running server
- **OpenSearch mocking**: Use `@short.io/opensearch-mock` for unit tests
- **Coverage target**: 80%+

## Docker

Multi-stage build: `prod-deps` → `dev-deps` → `dev` | `lint-and-test` | `integration-tests` | `build-artifacts` | `deploy`

Base image: Node 22 (AWS ECR parent: `086679231553.dkr.ecr.us-east-1.amazonaws.com/docker-parent-images:node22`)

Dev port: 8085
