# GraphQL Service Agent

Expert agent for working in any `graphql-*` Node.js backend service (except graphql-router).

## Context

You are working in an Apollo GraphQL server that is part of a federated supergraph. The service follows standardized patterns from @theorchard packages.

## Service Identification

Identify which service you're in by checking `package.json` name:
- `graphql-analytics` — Analytics aggregation (5 OWS connectors)
- `graphql-knowledge-search` — OpenSearch-based search
- `graphql-knowledge` — Neo4j + Snowflake knowledge graph + Kafka events
- `graphql-product` — Product catalog (25+ OWS connectors, largest service)
- `graphql-user` — User management (Neo4j + OWS)

## Standard Architecture

All services follow this structure:
```
src/
├── connectors/     # DataSource per backend (OWS, Neo4j, OpenSearch, etc.)
├── resolvers/      # GraphQL resolvers with types/, enums/, utils/
├── schema/         # Modular .graphql files (schema-first)
├── constants/      # URLs, TTLs, feature flags
├── generated/      # Auto-generated types (DO NOT EDIT)
└── config.ts       # Environment config
```

## Key Patterns to Follow

### Adding a New GraphQL Field

1. Add field to `.graphql` schema file in `src/schema/`
2. If new type: create `*Key` interface in `src/resolvers/types/` and register in codegen config
3. If new data source: create connector in `src/connectors/` with Zod schema + DataLoader
4. Write resolver satisfying `Partial<QueryResolvers>` or `<TypeName>Resolvers`
5. Run `yarn generate:types` to regenerate TypeScript
6. Write unit tests in `src/resolvers/__tests__/`

### Adding a New Connector

1. Create directory `src/connectors/<service-name>/`
2. Create DataSource class (extend `@theorchard/datasource-ows` for OWS, or custom)
3. Add Zod schemas for response validation (`.transform()` for snake_case → camelCase)
4. Create DataLoader factory: `create<Name>DataLoader(post)` with `cacheKeyFn`
5. Register DataSource in context creation (`src/context.ts` or equivalent)
6. Write tests for Zod schema (valid + invalid), cacheKeyFn, and DataLoader

### Zod Schema Convention

```typescript
const responseSchema = z.object({
    some_field: z.string(),
    nested_data: z.object({ ... }),
}).transform(data => ({
    someField: data.some_field,
    nestedData: data.nested_data,
}));
```

### DataLoader Factory Convention

```typescript
export const dataSchema = z.object({ ... });
export type MyDataLoader = ZodDataLoader<typeof dataSchema>;
export const cacheKeyFn = (key: string) => `TypeName:${key}`;
export const createMyDataLoader = (post: PostFn): MyDataLoader =>
    new ZodDataLoader(dataSchema, { cacheKeyFn, batchFn: ... });
```

## Commands

```bash
yarn start              # Dev server
yarn build              # Production build
yarn test:unit          # Unit tests
yarn test:integration   # Integration tests
yarn generate:types     # Regenerate GraphQL types
yarn lint               # ESLint + schema linting
```

## Common Mistakes to Avoid

- Do NOT edit files in `src/generated/` — they are auto-generated
- Do NOT skip Zod validation for external API responses
- Do NOT hardcode OWS URLs — use environment variables via config.ts
- Do NOT forget to update codegen config when adding new mapper types
- Do NOT create resolvers without corresponding unit tests
- Always run `yarn generate:types` after modifying `.graphql` files
