# Coder Subagent Reference

## Quick Navigation

| Category     | Key Files                                                          |
| ------------ | ------------------------------------------------------------------ |
| Routes       | `src/routes/*.routes.ts`                                           |
| Services     | `src/services/*.server.ts` (backend), `src/services/*.ts` (shared) |
| Repositories | `src/services/db/*.db.ts`                                          |
| Types        | `src/types.ts`, `src/types/*.ts`                                   |
| Schemas      | `src/schemas/*.schemas.ts`                                         |
| Config       | `src/config/env.ts`                                                |
| Utils        | `src/utils/*.ts`                                                   |

---

## Architecture Patterns (ADR Summary)

### Layer Flow

```
Route → validateRequest(schema) → Service → Repository → Database
                                    ↓
                              External APIs
```

### Repository Naming Convention (ADR-034)

| Prefix    | Behavior                          | Return         |
| --------- | --------------------------------- | -------------- |
| `get*`    | Throws `NotFoundError` if missing | Entity         |
| `find*`   | Returns `null` if missing         | Entity \| null |
| `list*`   | Returns empty array if none       | Entity[]       |
| `create*` | Creates new record                | Entity         |
| `update*` | Updates existing                  | Entity         |
| `delete*` | Removes record                    | void           |

### Error Handling (ADR-001)

```typescript
import { handleDbError, NotFoundError } from '../services/db/errors.db';
import { logger } from '../services/logger.server';

// Repository pattern
async function getById(id: string): Promise<Entity> {
  try {
    const result = await pool.query('SELECT * FROM table WHERE id = $1', [id]);
    if (result.rows.length === 0) throw new NotFoundError('Entity not found');
    return result.rows[0];
  } catch (error) {
    handleDbError(error, logger, 'Failed to get entity', { id });
  }
}
```

### API Response Helpers (ADR-028)

```typescript
import {
  sendSuccess,
  sendPaginated,
  sendError,
  sendNoContent,
  sendMessage,
  ErrorCode,
} from '../utils/apiResponse';

// Success with data
sendSuccess(res, data); // 200
sendSuccess(res, data, 201); // 201 Created

// Paginated
sendPaginated(res, items, { page, limit, total });

// Error
sendError(res, ErrorCode.NOT_FOUND, 'User not found', 404);
sendError(res, ErrorCode.VALIDATION_ERROR, 'Invalid input', 400, validationErrors);

// No content / Message
sendNoContent(res); // 204
sendMessage(res, 'Password updated');
```

### Transaction Pattern (ADR-002)

```typescript
import { withTransaction } from '../services/db/connection.db';

const result = await withTransaction(async (client) => {
  await client.query('INSERT INTO a ...');
  await client.query('INSERT INTO b ...');
  return { success: true };
});
```

---

## Adding New Features

### New API Endpoint Checklist

1. **Schema** (`src/schemas/{domain}.schemas.ts`)

   ```typescript
   import { z } from 'zod';
   export const createEntitySchema = z.object({
     body: z.object({ name: z.string().min(1) }),
   });
   ```

2. **Route** (`src/routes/{domain}.routes.ts`)

   ```typescript
   import { validateRequest } from '../middleware/validation.middleware';
   import { createEntitySchema } from '../schemas/{domain}.schemas';

   router.post('/', validateRequest(createEntitySchema), async (req, res, next) => {
     try {
       const result = await entityService.create(req.body);
       sendSuccess(res, result, 201);
     } catch (error) {
       next(error);
     }
   });
   ```

3. **Service** (`src/services/{domain}Service.server.ts`)

   ```typescript
   export async function create(data: CreateInput): Promise<Entity> {
     // Business logic here
     return repository.create(data);
   }
   ```

4. **Repository** (`src/services/db/{domain}.db.ts`)
   ```typescript
   export async function create(data: CreateInput, client?: PoolClient): Promise<Entity> {
     const pool = client || getPool();
     try {
       const result = await pool.query('INSERT INTO ...', [data.name]);
       return result.rows[0];
     } catch (error) {
       handleDbError(error, logger, 'Failed to create entity', { data });
     }
   }
   ```

### New Background Job Checklist

1. **Queue** (`src/services/queues.server.ts`)

   ```typescript
   export const myQueue = new Queue('my-queue', { connection: redisConnection });
   ```

2. **Worker** (`src/services/workers.server.ts`)

   ```typescript
   new Worker(
     'my-queue',
     async (job) => {
       // Process job
     },
     { connection: redisConnection },
   );
   ```

3. **Trigger** (in service)
   ```typescript
   await myQueue.add('job-name', { data });
   ```

---

## Key Files Reference

### Database Repositories

| Repository           | Purpose                       | Path                                 |
| -------------------- | ----------------------------- | ------------------------------------ |
| `flyer.db.ts`        | Flyer CRUD, processing status | `src/services/db/flyer.db.ts`        |
| `store.db.ts`        | Store management              | `src/services/db/store.db.ts`        |
| `user.db.ts`         | User accounts                 | `src/services/db/user.db.ts`         |
| `shopping.db.ts`     | Shopping lists, watchlists    | `src/services/db/shopping.db.ts`     |
| `gamification.db.ts` | Achievements, points          | `src/services/db/gamification.db.ts` |
| `category.db.ts`     | Item categories               | `src/services/db/category.db.ts`     |
| `price.db.ts`        | Price history, comparisons    | `src/services/db/price.db.ts`        |
| `recipe.db.ts`       | Recipe management             | `src/services/db/recipe.db.ts`       |

### Services

| Service                            | Purpose                          | Path                                            |
| ---------------------------------- | -------------------------------- | ----------------------------------------------- |
| `flyerProcessingService.server.ts` | Orchestrates flyer AI extraction | `src/services/flyerProcessingService.server.ts` |
| `flyerAiProcessor.server.ts`       | Gemini AI integration            | `src/services/flyerAiProcessor.server.ts`       |
| `cacheService.server.ts`           | Redis caching                    | `src/services/cacheService.server.ts`           |
| `queues.server.ts`                 | BullMQ queue definitions         | `src/services/queues.server.ts`                 |
| `workers.server.ts`                | BullMQ workers                   | `src/services/workers.server.ts`                |
| `emailService.server.ts`           | Nodemailer integration           | `src/services/emailService.server.ts`           |
| `geocodingService.server.ts`       | Address geocoding                | `src/services/geocodingService.server.ts`       |

### Routes

| Route              | Base Path     | Auth Required |
| ------------------ | ------------- | ------------- |
| `flyer.routes.ts`  | `/api/flyers` | Mixed         |
| `store.routes.ts`  | `/api/stores` | Mixed         |
| `user.routes.ts`   | `/api/users`  | Yes           |
| `auth.routes.ts`   | `/api/auth`   | No            |
| `admin.routes.ts`  | `/api/admin`  | Admin only    |
| `deals.routes.ts`  | `/api/deals`  | No            |
| `health.routes.ts` | `/api/health` | No            |

---

## Error Types

| Error Class                 | HTTP Status | Use Case                  |
| --------------------------- | ----------- | ------------------------- |
| `NotFoundError`             | 404         | Resource not found        |
| `ForbiddenError`            | 403         | Access denied             |
| `ValidationError`           | 400         | Input validation failed   |
| `UniqueConstraintError`     | 409         | Duplicate record          |
| `ForeignKeyConstraintError` | 400         | Referenced record missing |
| `NotNullConstraintError`    | 400         | Required field null       |

Import: `import { NotFoundError, ... } from '../services/db/errors.db'`

---

## Middleware

| Middleware                | Purpose              | Usage                                                        |
| ------------------------- | -------------------- | ------------------------------------------------------------ |
| `validateRequest(schema)` | Zod validation       | `router.post('/', validateRequest(schema), handler)`         |
| `requireAuth`             | JWT authentication   | `router.get('/', requireAuth, handler)`                      |
| `requireAdmin`            | Admin role check     | `router.delete('/', requireAuth, requireAdmin, handler)`     |
| `fileUpload`              | Multer file handling | `router.post('/upload', fileUpload.single('file'), handler)` |

---

## Type Definitions

| File                        | Contains                                        |
| --------------------------- | ----------------------------------------------- |
| `src/types.ts`              | Main types: User, Flyer, FlyerItem, Store, etc. |
| `src/types/api.ts`          | API response envelopes, pagination              |
| `src/types/auth.ts`         | Auth-related types                              |
| `src/types/gamification.ts` | Achievement types                               |

---

## Naming Conventions (ADR-027)

| Context           | Convention       | Example                             |
| ----------------- | ---------------- | ----------------------------------- |
| AI output types   | `Ai*` prefix     | `AiFlyerItem`, `AiExtractionResult` |
| Database types    | `Db*` prefix     | `DbFlyer`, `DbUser`                 |
| API types         | No prefix        | `Flyer`, `User`                     |
| Schema validation | `*Schema` suffix | `createFlyerSchema`                 |
| Routes            | `*.routes.ts`    | `flyer.routes.ts`                   |
| Repositories      | `*.db.ts`        | `flyer.db.ts`                       |
| Server services   | `*.server.ts`    | `aiService.server.ts`               |
| Client services   | `*.client.ts`    | `logger.client.ts`                  |
