| name | sea-handler-gen |
| description | Generate SEA handlers from specifications. Creates type-safe, validated handlers with proper error handling and governance compliance. |
SEA Handler Generation
Generate type-safe, validated handlers from SEA specifications. Handlers include proper error handling, validation, logging, and governance compliance.
When to Use
Invoke when:
- Spec has been updated
- New API endpoints needed
- Handlers need regeneration
- After spec validation
Usage
User invokes with:
- "Generate handlers for {context}"
- "Update handlers from spec"
- "Regenerate {context} handlers"
Claude invokes automatically:
- After spec changes are committed
- When user says "pipeline {context}"
- Before testing new API surface
Generation Process
Step 1: Load and Validate Spec
SPEC_FILE="apps/$CONTEXT/spec/context.yaml"
if [ ! -f "$SPEC_FILE" ]; then
echo "❌ Spec not found: $SPEC_FILE"
exit 1
fi
yq eval '.' "$SPEC_FILE" > /dev/null || exit 1
calm validate "$SPEC_FILE" 2>/dev/null || true
echo "✅ Spec validated: $SPEC_FILE"
Step 2: Parse API Surface
API_SURFACE=$(yq eval '.api_surface' "$SPEC_FILE")
ENDPOINTS=$(echo "$API_SURFACE" | yq eval '.endpoints[]')
SCHEMAS=$(echo "$API_SURFACE" | yq eval '.schemas[]')
EVENTS=$(echo "$API_SURFACE" | yq eval '.events[]')
Step 3: Generate Handler Template
For each endpoint, generate a handler:
import { z } from 'zod';
import { Context } from '@sprime01/sea';
import type { Request, Response } from 'express';
const RequestSchema = z.object({
});
const ResponseSchema = z.object({
});
export interface {EndpointName}Handler {
handle(request: Request): Promise<Response>;
}
export class {EndpointName}Handler implements {EndpointName}Handler {
constructor(
private readonly context: Context,
private readonly logger: Logger
) {}
async handle(request: Request): Promise<Response> {
try {
const validated = RequestSchema.parse(request.body);
const result = await this.execute(validated);
const response = ResponseSchema.parse(result);
return {
status: 200,
body: response
};
} catch (error) {
this.logger.error('Handler error', { error, context: request.body });
if (error instanceof z.ZodError) {
return {
status: 400,
body: {
error: 'Validation error',
details: error.errors
}
};
}
return {
status: 500,
body: {
error: 'Internal server error'
}
};
}
}
private async execute(input: z.infer<typeof RequestSchema>) {
throw new Error('Not implemented');
}
}
Step 4: Generate Type Definitions
import { z } from 'zod';
export const CreateUserRequestSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
preferences: z.record(z.unknown()).optional()
});
export const UserResponseSchema = z.object({
id: z.string().uuid(),
name: z.string(),
email: z.string(),
createdAt: z.string().datetime()
});
export interface User {
id: string;
name: string;
email: string;
preferences: Record<string, unknown>;
createdAt: string;
}
Step 5: Generate Express Router
import { Router } from 'express';
import { {EndpointName}Handler } from './handlers/{endpoint-name}';
import { Context } from '@sprime01/sea';
export function create{Context}Router(context: Context): Router {
const router = Router();
const handler = new {EndpointName}Handler(context, context.logger);
router.post('/api/{endpoint}', async (req, res) => {
const response = await handler.handle(req);
res.status(response.status).json(response.body);
});
return router;
}
Step 6: Generate Test Templates
import { describe, it, expect, beforeEach } from 'vitest';
import { {EndpointName}Handler } from '../../src/gen/handlers/{endpoint-name}';
describe('{EndpointName}Handler', () => {
let handler: {EndpointName}Handler;
beforeEach(() => {
handler = new {EndpointName}Handler(
mockContext,
mockLogger
);
});
describe('handle', () => {
it('should validate request schema', async () => {
const request = {
body: { }
};
const response = await handler.handle(request);
expect(response.status).toBe(200);
});
it('should reject invalid request', async () => {
const request = {
body: { }
};
const response = await handler.handle(request);
expect(response.status).toBe(400);
});
it('should handle domain errors', async () => {
});
});
});
Step 7: Update Generated Zone Guard
Add .SEA-PROTECTED marker to all generated files:
Handler Patterns
CRUD Handlers
export class Create{Entity}Handler {
async handle(request: Create{Entity}Request): Promise<{Entity}Response> {
const entity = await this.domain.create(request.body);
return this.serializer.serialize(entity);
}
}
export class Read{Entity}Handler {
async handle(request: Read{Entity}Request): Promise<{Entity}Response> {
const entity = await this.domain.findById(request.params.id);
if (!entity) {
throw new NotFoundError('Entity not found');
}
return this.serializer.serialize(entity);
}
}
export class Update{Entity}Handler {
async handle(request: Update{Entity}Request): Promise<{Entity}Response> {
const entity = await this.domain.update(request.params.id, request.body);
return this.serializer.serialize(entity);
}
}
export class Delete{Entity}Handler {
async handle(request: Delete{Entity}Request): Promise<void> {
await this.domain.delete(request.params.id);
}
}
Event Handlers
export class {EventName}Handler {
async handle(event: {EventName}Event): Promise<void> {
const validated = {EventName}Schema.parse(event);
await this.domain.processEvent(validated);
await this.eventBus.publish({
type: '{EventName}Processed',
payload: validated
});
}
}
Validation
After generation, validate:
tsc --noEmit
eslint src/gen/
vitest --run tests/
Integration
- Integrates with
sea-generator-first for pipeline execution
- Integrates with
spec-guardian for generated zone protection
- Integrates with
governance-validation for CALM compliance
- Integrates with
zod for runtime validation
Output
After generation:
## Handlers Generated
**Context**: {context}
**Spec**: spec/context.yaml
**Version**: {version}
### Generated Files
- Handlers: {count}
- Types: {count}
- Router: 1
- Tests: {count}
### Handlers
- {endpoint1}: Create{Entity}Handler
- {endpoint2}: Read{Entity}Handler
- {endpoint3}: Update{Entity}Handler
- {endpoint4}: Delete{Entity}Handler
### Validation
✅ TypeScript compilation passed
✅ All schemas validated
✅ Tests templates generated
### Next Steps
1. Implement business logic in domain layer
2. Run tests: pnpm test
3. Test API surface: pnpm test:e2e