Files
flyer-crawler.projectium.com/docs/SUBAGENT-CODER-REFERENCE.md

10 KiB

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)

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)

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)

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)

    import { z } from 'zod';
    export const createEntitySchema = z.object({
      body: z.object({ name: z.string().min(1) }),
    });
    
  2. Route (src/routes/{domain}.routes.ts)

    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)

    export async function create(data: CreateInput): Promise<Entity> {
      // Business logic here
      return repository.create(data);
    }
    
  4. Repository (src/services/db/{domain}.db.ts)

    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)

    export const myQueue = new Queue('my-queue', { connection: redisConnection });
    
  2. Worker (src/services/workers.server.ts)

    new Worker(
      'my-queue',
      async (job) => {
        // Process job
      },
      { connection: redisConnection },
    );
    
  3. Trigger (in service)

    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