# Pattern Audit: `packages/vsr-graphql-server` vs Orchard Org Standards

> **Date:** June 3, 2026
> **Auditor:** Sub-agent exploration pass (Explore agent)
> **Reference repos:** `research/other_repos/orchard-suite/packages/connector-neo4j/`, `research/other_repos/orchard-suite/apps/`
> **Subject:** `packages/vsr-graphql-server/`

---

## Summary

The VSR GraphQL server implements a functional Apollo Server 4 + PostgreSQL/Snowflake stack. The schema loading pattern, resolver export structure, pnpm workspace integration, SQL parameterisation, and TypeScript strict mode all match org conventions. Five divergences were found, two of which require a decision before implementation changes are made.

---

## Dimension-by-Dimension Findings

| # | Dimension | Status | Finding | File References |
|---|-----------|--------|---------|-----------------|
| 1 | **Unused dependency** | ❌ Diverges | `@theorchard/graphql-server: "*"` appears in `package.json` dependencies but is never imported in any source file. Dead dependency. | `packages/vsr-graphql-server/package.json` L11 |
| 2 | **Data source pattern** | ❌ Diverges | VSR uses class-based singletons: `export const rdsDataSource = new RdsDataSource()` (instantiated in file). Org pattern (`connector-neo4j`) uses function-based init/getter: `initNeo4jDriver()` + `getNeo4jDriver(): Driver`. | VSR: `src/dataSources/RdsDataSource.ts`, `src/dataSources/SnowflakeDataSource.ts`; Org ref: `research/other_repos/orchard-suite/packages/connector-neo4j/src/neo4j.ts` L24–80 |
| 3 | **Server boot pattern** | ❌ Diverges | VSR uses `startStandaloneServer` from `@apollo/server/standalone` — Apollo's development/demo pattern, not production-grade. No org reference uses this pattern. Should use `expressMiddleware` or the `@theorchard/graphql-server` wrapper (already listed as a dep). | `src/index.ts` L17, L62–68 |
| 4 | **TypeScript module output** | ❌ Diverges | `tsconfig.json` sets `"module": "CommonJS"`. Org base tsconfig uses `"module": "ES2020"`. `connector-neo4j` produces a dual CJS + ESM build via separate `tsconfig.cjs.json` / `tsconfig.esm.json`. | `packages/vsr-graphql-server/tsconfig.json` L6; Org: `research/other_repos/orchard-suite/packages/connector-neo4j/` |
| 5 | **Linting / formatting** | ⚠️ Known deviation | VSR uses Biome (`biome.json`). Org root uses `@theorchard/eslint-config-ts-prettier` + Prettier 3.2.5. **This divergence is intentional** — Biome is explicitly listed as the VSR target stack in INT-2682 instructions. Requires an ADR. | VSR: `biome.json`, root `package.json`; Org: `research/other_repos/orchard-suite/package.json` L46–47, L78 |
| 6 | **Schema loading** | ✅ Matches | Uses `loadFilesSync()` + `mergeTypeDefs()` + `makeExecutableSchema()` from `@graphql-tools/*` — org standard for modular schema loading. | `src/index.ts` L19–22 |
| 7 | **Resolver file organisation** | ✅ Matches | One file per domain (`productResolvers`, `cartOrderResolvers`, `userResolvers`), each exporting `Query`/`Mutation` objects merged via `mergeResolvers()`. | `src/resolvers/` |
| 8 | **pnpm workspace integration** | ✅ Matches | Uses workspace `"*"` refs correctly. No duplicate `pnpm-lock.yaml` at package level. | `packages/vsr-graphql-server/package.json` L12, L14 |
| 9 | **SQL parameterisation** | ✅ Matches | All PostgreSQL queries use `$N` positional binds. No interpolation. | `src/dataSources/RdsDataSource.ts` |
| 10 | **TypeScript strict mode** | ✅ Matches | `"strict": true` matches org base config. | `packages/vsr-graphql-server/tsconfig.json` |
| 11 | **Node version constraint** | ✅ Matches | `engines: { node: ">=24.0.0" }` matches org standard. | `packages/vsr-graphql-server/package.json` |
| 12 | **Context / auth guard** | ⚠️ Pattern unclear | Custom `buildContext(req)` with DEV_MODE bypass and per-resolver `requiresAuth` guards. No GraphQL-server auth middleware found in reference repos to validate against. Deferred to INT-2697 (`@theorchard/suite-auth` integration). | `src/middleware/authGuard.ts` L23–62 |
| 13 | **Test framework** | ⚠️ Partial | VSR uses Vitest 3.1.1 via `@theorchard/vitest-config`. Org uses Jest 29.4.3 as primary for packages. Vitest is present in the orchard-suite but Jest is the dominant pattern. Acceptable, not blocking. | `packages/vsr-graphql-server/vitest.config.ts` |
| 14 | **Dev script** | ⚠️ Partial | Uses raw `ts-node-dev` + dotenv. Org apps use `@theorchard/frontend-cli*`. Acceptable for a server package (not a frontend app), but worth reviewing. | `packages/vsr-graphql-server/package.json` L4 |
| 15 | **Logging** | ⚠️ Partial | Raw `console.*` throughout. No structured logger. Org reference pattern is also `console.*` (no pino/winston found in refs), so this is low-priority. | `src/index.ts` L75–76, `src/middleware/authGuard.ts` L48, `src/dataSources/CdsAdapter.ts` L52 |
| 16 | **`ping` query** | ⚠️ Unclear | `Query.ping: String!` exists in schema and resolver — lab convenience, not identified as an org standard. Confirm whether it belongs in the production schema. | `src/schema/root.graphql` L3–4, `src/resolvers/productResolvers.ts` L55–56 |

---

## Required Changes (must-fix before PR)

### 1. Remove unused dependency
- **File:** `packages/vsr-graphql-server/package.json` L11
- **Action:** Delete the `@theorchard/graphql-server: "*"` line. It is never imported in any source file.

### 2. Refactor data sources to init/getter pattern
- **Files:** `src/dataSources/RdsDataSource.ts`, `src/dataSources/SnowflakeDataSource.ts`
- **Reference:** `research/other_repos/orchard-suite/packages/connector-neo4j/src/neo4j.ts` L24–80
- **Action:** Replace class-based singletons with named init/getter functions:
  - `export const initRds(): void` and `export const getRdsPool(): Pool`
  - `export const initSnowflake(): void` (already exists) and `export const getSnowflakeConnection(): Connection`
  - Update `src/index.ts` boot sequence and resolver call sites accordingly.

### 3. Replace `startStandaloneServer` with production server boot — **decision required**
- **File:** `src/index.ts` L17, L62–68
- **Decision needed:** The `@theorchard/graphql-server` package is already listed as a dep (even if currently unused). Two options:
  - **Option A:** Wire `@theorchard/graphql-server` in place of `startStandaloneServer` — aligns with org pattern, uses the existing dep.
  - **Option B:** Replace with `expressMiddleware` from `@apollo/server/express4` — standard Apollo production pattern, no org package required.
- Log decision as an ADR once chosen.

### 4. Fix TypeScript module output
- **File:** `packages/vsr-graphql-server/tsconfig.json` L6
- **Action:** Change `"module": "CommonJS"` to `"module": "ES2020"`. If a CommonJS build is needed for Node compatibility, add a `tsconfig.cjs.json` (matching `connector-neo4j` dual-build pattern).

---

## Decisions Required Before Implementation

| Decision | Options | Implication |
|----------|---------|-------------|
| **Server boot** (#3 above) | A: `@theorchard/graphql-server` wrapper / B: `expressMiddleware` | Affects `src/index.ts` structure; log as ADR |
| **Biome vs ESLint** (#5 above) | Keep Biome (VSR intent) / Realign to org ESLint | Intentional deviation — needs ADR to close; no code change required if Biome is confirmed |

---

## Recommended Changes (should-fix)

1. **Migrate to Jest** — Org primary test framework is Jest 29. Vitest is present but Jest dominates package tests in `orchard-suite`. Migrate `vitest.config.ts` → `jest.config.js` using org config.

2. **Add structured logger** — Replace `console.*` with pino or a thin org-provided logger for production-quality observability.

3. **Review `ping` query scope** — Confirm with team whether `Query.ping` belongs in the production schema or should be an internal/health-check-only endpoint.

4. **Auth guard alignment** — Defer to INT-2697. The custom `buildContext` pattern should be reconciled with `@theorchard/suite-auth` once the external B2B auth scope is resolved.

---

## Confirmed Matches ✅

| Dimension | Notes |
|-----------|-------|
| Schema loading | `loadFilesSync` + `mergeTypeDefs` + `makeExecutableSchema` — org standard |
| Resolver export structure | One-file-per-domain, `mergeResolvers()` — matches org conventions |
| pnpm workspace integration | Correct `workspace: "*"` refs, no duplicate lock file |
| SQL parameterisation | All queries use `$N` positional binds — safe from injection |
| `strict: true` TypeScript | Matches org base config |
| Node ≥24 engine constraint | Matches org standard |

---

## Implementation Priority

| Phase | Items |
|-------|-------|
| **A — Blocking** | #1 unused dep, #2 data source pattern, #3 server boot (after decision), #4 module output |
| **B — Soon after** | Jest migration, structured logger, `ping` query scope |
| **C — INT-2697 onwards** | Suite-auth alignment, Biome ADR |
