con un clic
new-service
Scaffold a service class for a given domain following project conventions
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Scaffold a service class for a given domain following project conventions
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Implement a numbered testing strategy phase from docs/testing_strategy_phases/phase_$ARGUMENTS_*.md step by step
Implement a numbered phase from docs/phases/phase-$ARGUMENTS.md step by step
Review changed code for correctness, security, and project conventions. Invoke this skill automatically whenever a complete unit of work is done — a full implementation phase, a complete feature (repository + service + route + component all connected), or just before creating a PR. Do NOT invoke after editing a single file or mid-way through a feature. If the user says "done", "finished", "phase complete", "ready to commit", or similar, trigger this review immediately.
Run prisma migrate dev, regenerate client to the correct output path, and verify import paths
Scaffold a Prisma-backed repository for a given Prisma model
Final pre-commit check: run type-check (npx tsc --noEmit), lint (npm run lint), and tests (npm test)
| name | new-service |
| description | Scaffold a service class for a given domain following project conventions |
| disable-model-invocation | true |
| argument-hint | [domain-name] |
Scaffold a service for the $ARGUMENTS domain.
src/services/$ARGUMENTS.service.tssrc/types/index.ts (add alongside existing schemas)import { ${camelCase}Repository } from '@/repositories/$ARGUMENTS.repository'
import { createInputSchema, type CreateInput } from '@/types'
export const ${camelCase}Service = {
async create(userId: string, input: CreateInput) {
createInputSchema.parse(input) // validate first
return ${camelCase}Repository.create({ ...input, userId })
},
async getById(id: string, userId: string) {
const record = await ${camelCase}Repository.findById(id)
if (!record) throw new Error('Not found')
if (record.userId !== userId) throw new Error('Unauthorized') // AFTER fetch
return record
},
async listForUser(userId: string) {
return ${camelCase}Repository.findMany({ userId })
},
async update(id: string, userId: string, input: CreateInput) {
createInputSchema.parse(input)
const existing = await ${camelCase}Repository.findById(id)
if (!existing) throw new Error('Not found')
if (existing.userId !== userId) throw new Error('Unauthorized')
return ${camelCase}Repository.update(id, input)
},
async delete(id: string, userId: string) {
const existing = await ${camelCase}Repository.findById(id)
if (!existing) throw new Error('Not found')
if (existing.userId !== userId) throw new Error('Unauthorized')
return ${camelCase}Repository.delete(id)
},
}
The check goes AFTER the fetch, not before. This is intentional:
'Not found' (becomes 404)'Unauthorized' (becomes 403)
Checking before the fetch would collapse both cases into 403 and leak that a record exists.userId flows in as a parameterServices do NOT call authService.getUser() internally. The API route retrieves the user from the session and passes userId into every service method. This keeps services testable in isolation.
@/repositories/...@/typesprisma directly — that is the repository's job@prisma/clientDefine in src/types/index.ts:
export const createInvoiceSchema = z.object({ ... })
export type CreateInvoiceInput = z.infer<typeof createInvoiceSchema>
npx tsc --noEmit